@colyseus/schema 3.0.0-alpha.2 → 3.0.0-alpha.23

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 (103) hide show
  1. package/README.md +131 -61
  2. package/build/cjs/index.js +476 -150
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +475 -149
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +476 -150
  7. package/lib/Metadata.d.ts +2 -0
  8. package/lib/Metadata.js +39 -0
  9. package/lib/Metadata.js.map +1 -1
  10. package/lib/Reflection.d.ts +1 -2
  11. package/lib/Reflection.js +28 -22
  12. package/lib/Reflection.js.map +1 -1
  13. package/lib/Schema.d.ts +1 -1
  14. package/lib/annotations.js +12 -10
  15. package/lib/annotations.js.map +1 -1
  16. package/lib/bench_encode.d.ts +1 -0
  17. package/lib/bench_encode.js +93 -0
  18. package/lib/bench_encode.js.map +1 -0
  19. package/lib/codegen/api.js +1 -2
  20. package/lib/codegen/api.js.map +1 -1
  21. package/lib/codegen/languages/cpp.js +1 -2
  22. package/lib/codegen/languages/cpp.js.map +1 -1
  23. package/lib/codegen/languages/csharp.js +1 -2
  24. package/lib/codegen/languages/csharp.js.map +1 -1
  25. package/lib/codegen/languages/haxe.js +1 -2
  26. package/lib/codegen/languages/haxe.js.map +1 -1
  27. package/lib/codegen/languages/java.js +1 -2
  28. package/lib/codegen/languages/java.js.map +1 -1
  29. package/lib/codegen/languages/js.js +1 -2
  30. package/lib/codegen/languages/js.js.map +1 -1
  31. package/lib/codegen/languages/lua.js +1 -2
  32. package/lib/codegen/languages/lua.js.map +1 -1
  33. package/lib/codegen/languages/ts.js +1 -2
  34. package/lib/codegen/languages/ts.js.map +1 -1
  35. package/lib/codegen/parser.js +2 -3
  36. package/lib/codegen/parser.js.map +1 -1
  37. package/lib/codegen/types.js +3 -3
  38. package/lib/codegen/types.js.map +1 -1
  39. package/lib/debug.d.ts +1 -0
  40. package/lib/debug.js +52 -0
  41. package/lib/debug.js.map +1 -0
  42. package/lib/decoder/DecodeOperation.d.ts +0 -1
  43. package/lib/decoder/DecodeOperation.js +22 -7
  44. package/lib/decoder/DecodeOperation.js.map +1 -1
  45. package/lib/decoder/Decoder.d.ts +4 -5
  46. package/lib/decoder/Decoder.js +4 -4
  47. package/lib/decoder/Decoder.js.map +1 -1
  48. package/lib/decoder/strategy/RawChanges.js +1 -2
  49. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  50. package/lib/decoder/strategy/StateCallbacks.d.ts +36 -7
  51. package/lib/decoder/strategy/StateCallbacks.js +39 -46
  52. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  53. package/lib/encoder/ChangeTree.js +2 -5
  54. package/lib/encoder/ChangeTree.js.map +1 -1
  55. package/lib/encoder/EncodeOperation.d.ts +0 -1
  56. package/lib/encoder/EncodeOperation.js +29 -13
  57. package/lib/encoder/EncodeOperation.js.map +1 -1
  58. package/lib/encoder/Encoder.d.ts +5 -4
  59. package/lib/encoder/Encoder.js +60 -38
  60. package/lib/encoder/Encoder.js.map +1 -1
  61. package/lib/encoder/StateView.js +11 -6
  62. package/lib/encoder/StateView.js.map +1 -1
  63. package/lib/encoding/assert.js +3 -3
  64. package/lib/encoding/assert.js.map +1 -1
  65. package/lib/encoding/decode.d.ts +21 -19
  66. package/lib/encoding/decode.js +24 -25
  67. package/lib/encoding/decode.js.map +1 -1
  68. package/lib/encoding/encode.d.ts +2 -2
  69. package/lib/encoding/encode.js +40 -39
  70. package/lib/encoding/encode.js.map +1 -1
  71. package/lib/encoding/spec.d.ts +2 -1
  72. package/lib/encoding/spec.js +1 -0
  73. package/lib/encoding/spec.js.map +1 -1
  74. package/lib/index.d.ts +3 -0
  75. package/lib/index.js +5 -1
  76. package/lib/index.js.map +1 -1
  77. package/lib/types/custom/ArraySchema.d.ts +2 -2
  78. package/lib/types/custom/ArraySchema.js +0 -8
  79. package/lib/types/custom/ArraySchema.js.map +1 -1
  80. package/lib/types/registry.js +3 -4
  81. package/lib/types/registry.js.map +1 -1
  82. package/lib/types/utils.js +1 -2
  83. package/lib/types/utils.js.map +1 -1
  84. package/lib/utils.js +3 -4
  85. package/lib/utils.js.map +1 -1
  86. package/package.json +5 -5
  87. package/src/Metadata.ts +47 -0
  88. package/src/Reflection.ts +31 -22
  89. package/src/annotations.ts +6 -2
  90. package/src/bench_encode.ts +66 -0
  91. package/src/debug.ts +56 -0
  92. package/src/decoder/DecodeOperation.ts +26 -6
  93. package/src/decoder/Decoder.ts +8 -8
  94. package/src/decoder/strategy/StateCallbacks.ts +93 -53
  95. package/src/encoder/ChangeTree.ts +2 -5
  96. package/src/encoder/EncodeOperation.ts +29 -12
  97. package/src/encoder/Encoder.ts +71 -42
  98. package/src/encoder/StateView.ts +10 -7
  99. package/src/encoding/decode.ts +24 -25
  100. package/src/encoding/encode.ts +25 -22
  101. package/src/encoding/spec.ts +1 -0
  102. package/src/index.ts +5 -0
  103. package/src/types/custom/ArraySchema.ts +2 -1
@@ -29,6 +29,7 @@
29
29
  OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
30
30
  OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
31
31
  OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
32
+ OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
32
33
  })(exports.OPERATION || (exports.OPERATION = {}));
33
34
 
34
35
  Symbol.metadata ??= Symbol.for("Symbol.metadata");
@@ -145,6 +146,45 @@
145
146
  isDeprecated(metadata, field) {
146
147
  return metadata[field].deprecated === true;
147
148
  },
149
+ init(klass) {
150
+ //
151
+ // Used only to initialize an empty Schema (Encoder#constructor)
152
+ // TODO: remove/refactor this...
153
+ //
154
+ const metadata = {};
155
+ klass.constructor[Symbol.metadata] = metadata;
156
+ Object.defineProperty(metadata, -1, {
157
+ value: 0,
158
+ enumerable: false,
159
+ configurable: true,
160
+ });
161
+ },
162
+ initialize(constructor, parentMetadata) {
163
+ let metadata = constructor[Symbol.metadata] ?? Object.create(null);
164
+ // make sure inherited classes have their own metadata object.
165
+ if (constructor[Symbol.metadata] === parentMetadata) {
166
+ metadata = Object.create(null);
167
+ if (parentMetadata) {
168
+ // assign parent metadata to current
169
+ Object.assign(metadata, parentMetadata);
170
+ for (let i = 0; i <= parentMetadata[-1]; i++) {
171
+ Object.defineProperty(metadata, i, {
172
+ value: parentMetadata[i],
173
+ enumerable: false,
174
+ configurable: true,
175
+ });
176
+ }
177
+ Object.defineProperty(metadata, -1, {
178
+ value: parentMetadata[-1],
179
+ enumerable: false,
180
+ configurable: true,
181
+ writable: true,
182
+ });
183
+ }
184
+ }
185
+ constructor[Symbol.metadata] = metadata;
186
+ return metadata;
187
+ },
148
188
  isValidInstance(klass) {
149
189
  return (klass.constructor[Symbol.metadata] &&
150
190
  Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], -1));
@@ -264,14 +304,12 @@
264
304
  this.checkIsFiltered(parent, parentIndex);
265
305
  if (!this.isFiltered) {
266
306
  this.root.changes.set(this, this.changes);
307
+ this.root.allChanges.set(this, this.allChanges);
267
308
  }
268
309
  if (this.isFiltered || this.isPartiallyFiltered) {
269
310
  this.root.filteredChanges.set(this, this.filteredChanges);
270
311
  this.root.allFilteredChanges.set(this, this.filteredChanges);
271
312
  }
272
- else {
273
- this.root.allChanges.set(this, this.allChanges);
274
- }
275
313
  this.ensureRefId();
276
314
  this.forEachChild((changeTree, atIndex) => {
277
315
  changeTree.setParent(this.ref, root, atIndex);
@@ -363,7 +401,6 @@
363
401
  }
364
402
  _shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
365
403
  Array.from(allChangeSet.entries()).forEach(([index, op]) => {
366
- // console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
367
404
  if (index >= startIndex) {
368
405
  allChangeSet.delete(index);
369
406
  allChangeSet.set(index + shiftIndex, op);
@@ -504,7 +541,7 @@
504
541
  }
505
542
  checkIsFiltered(parent, parentIndex) {
506
543
  // Detect if current structure has "filters" declared
507
- this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
544
+ this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
508
545
  // TODO: support "partially filtered", where the instance is visible, but only a field is not.
509
546
  // Detect if parent has "filters" declared
510
547
  while (parent && !this.isFiltered) {
@@ -565,26 +602,29 @@
565
602
  textEncoder = new TextEncoder();
566
603
  }
567
604
  catch (e) { }
568
- function utf8Length(str) {
569
- var c = 0, length = 0;
570
- for (var i = 0, l = str.length; i < l; i++) {
571
- c = str.charCodeAt(i);
572
- if (c < 0x80) {
573
- length += 1;
574
- }
575
- else if (c < 0x800) {
576
- length += 2;
577
- }
578
- else if (c < 0xd800 || c >= 0xe000) {
579
- length += 3;
580
- }
581
- else {
582
- i++;
583
- length += 4;
605
+ const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
606
+ const utf8Length = (hasBufferByteLength)
607
+ ? Buffer.byteLength // node
608
+ : function (str, _) {
609
+ var c = 0, length = 0;
610
+ for (var i = 0, l = str.length; i < l; i++) {
611
+ c = str.charCodeAt(i);
612
+ if (c < 0x80) {
613
+ length += 1;
614
+ }
615
+ else if (c < 0x800) {
616
+ length += 2;
617
+ }
618
+ else if (c < 0xd800 || c >= 0xe000) {
619
+ length += 3;
620
+ }
621
+ else {
622
+ i++;
623
+ length += 4;
624
+ }
584
625
  }
585
- }
586
- return length;
587
- }
626
+ return length;
627
+ };
588
628
  function utf8Write(view, str, it) {
589
629
  var c = 0;
590
630
  for (var i = 0, l = str.length; i < l; i++) {
@@ -679,8 +719,7 @@
679
719
  if (!value) {
680
720
  value = "";
681
721
  }
682
- // let length = utf8Length(value);
683
- let length = Buffer.byteLength(value, "utf8");
722
+ let length = utf8Length(value, "utf8");
684
723
  let size = 0;
685
724
  // fixstr
686
725
  if (length < 0x20) {
@@ -791,23 +830,23 @@
791
830
 
792
831
  var encode = /*#__PURE__*/Object.freeze({
793
832
  __proto__: null,
794
- utf8Length: utf8Length,
795
- utf8Write: utf8Write,
796
- int8: int8$1,
797
- uint8: uint8$1,
833
+ boolean: boolean$1,
834
+ float32: float32$1,
835
+ float64: float64$1,
798
836
  int16: int16$1,
799
- uint16: uint16$1,
800
837
  int32: int32$1,
801
- uint32: uint32$1,
802
838
  int64: int64$1,
839
+ int8: int8$1,
840
+ number: number$1,
841
+ string: string$1,
842
+ uint16: uint16$1,
843
+ uint32: uint32$1,
803
844
  uint64: uint64$1,
804
- float32: float32$1,
805
- float64: float64$1,
845
+ uint8: uint8$1,
846
+ utf8Length: utf8Length,
847
+ utf8Write: utf8Write,
806
848
  writeFloat32: writeFloat32,
807
- writeFloat64: writeFloat64,
808
- boolean: boolean$1,
809
- string: string$1,
810
- number: number$1
849
+ writeFloat64: writeFloat64
811
850
  });
812
851
 
813
852
  class EncodeSchemaError extends Error {
@@ -949,6 +988,18 @@
949
988
  }
950
989
  const type = changeTree.getType(field);
951
990
  const value = changeTree.getValue(field);
991
+ // try { throw new Error(); } catch (e) {
992
+ // // only print if not coming from Reflection.ts
993
+ // if (!e.stack.includes("src/Reflection.ts")) {
994
+ // console.log("encodeKeyValueOperation -> ", {
995
+ // ref: changeTree.ref.constructor.name,
996
+ // field,
997
+ // operation: OPERATION[operation],
998
+ // value: value?.toJSON(),
999
+ // items: ref.toJSON(),
1000
+ // });
1001
+ // }
1002
+ // }
952
1003
  // TODO: inline this function call small performance gain
953
1004
  encodeValue(encoder, bytes, ref, type, value, field, operation, it);
954
1005
  };
@@ -958,15 +1009,19 @@
958
1009
  */
959
1010
  const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
960
1011
  const ref = changeTree.ref;
961
- if (hasView &&
962
- operation === exports.OPERATION.DELETE &&
963
- typeof (changeTree.getType(field)) !== "string") {
964
- // encode delete by refId (array of schemas)
965
- bytes[it.offset++] = exports.OPERATION.DELETE_BY_REFID;
966
- const value = ref['tmpItems'][field];
967
- const refId = value[$changes].refId;
968
- number$1(bytes, refId, it);
969
- return;
1012
+ const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
1013
+ let refOrIndex;
1014
+ if (useOperationByRefId) {
1015
+ refOrIndex = ref['tmpItems'][field][$changes].refId;
1016
+ if (operation === exports.OPERATION.DELETE) {
1017
+ operation = exports.OPERATION.DELETE_BY_REFID;
1018
+ }
1019
+ else if (operation === exports.OPERATION.ADD) {
1020
+ operation = exports.OPERATION.ADD_BY_REFID;
1021
+ }
1022
+ }
1023
+ else {
1024
+ refOrIndex = field;
970
1025
  }
971
1026
  // encode operation
972
1027
  bytes[it.offset++] = operation & 255;
@@ -975,7 +1030,7 @@
975
1030
  return;
976
1031
  }
977
1032
  // encode index
978
- number$1(bytes, field, it);
1033
+ number$1(bytes, refOrIndex, it);
979
1034
  // Do not encode value for DELETE operations
980
1035
  if (operation === exports.OPERATION.DELETE) {
981
1036
  return;
@@ -1015,9 +1070,9 @@
1015
1070
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1016
1071
  * SOFTWARE
1017
1072
  */
1018
- function utf8Read(bytes, offset, length) {
1073
+ function utf8Read(bytes, it, length) {
1019
1074
  var string = '', chr = 0;
1020
- for (var i = offset, end = offset + length; i < end; i++) {
1075
+ for (var i = it.offset, end = it.offset + length; i < end; i++) {
1021
1076
  var byte = bytes[i];
1022
1077
  if ((byte & 0x80) === 0x00) {
1023
1078
  string += String.fromCharCode(byte);
@@ -1052,6 +1107,7 @@
1052
1107
  // (do not throw error to avoid server/client from crashing due to hack attemps)
1053
1108
  // throw new Error('Invalid byte ' + byte.toString(16));
1054
1109
  }
1110
+ it.offset += length;
1055
1111
  return string;
1056
1112
  }
1057
1113
  function int8(bytes, it) {
@@ -1119,9 +1175,7 @@
1119
1175
  else if (prefix === 0xdb) {
1120
1176
  length = uint32(bytes, it);
1121
1177
  }
1122
- const value = utf8Read(bytes, it.offset, length);
1123
- it.offset += length;
1124
- return value;
1178
+ return utf8Read(bytes, it, length);
1125
1179
  }
1126
1180
  function stringCheck(bytes, it) {
1127
1181
  const prefix = bytes[it.offset];
@@ -1225,30 +1279,31 @@
1225
1279
 
1226
1280
  var decode = /*#__PURE__*/Object.freeze({
1227
1281
  __proto__: null,
1228
- int8: int8,
1229
- uint8: uint8,
1230
- int16: int16,
1231
- uint16: uint16,
1232
- int32: int32,
1233
- uint32: uint32,
1282
+ arrayCheck: arrayCheck,
1283
+ boolean: boolean,
1234
1284
  float32: float32,
1235
1285
  float64: float64,
1286
+ int16: int16,
1287
+ int32: int32,
1236
1288
  int64: int64,
1237
- uint64: uint64,
1289
+ int8: int8,
1290
+ number: number,
1291
+ numberCheck: numberCheck,
1238
1292
  readFloat32: readFloat32,
1239
1293
  readFloat64: readFloat64,
1240
- boolean: boolean,
1241
1294
  string: string,
1242
1295
  stringCheck: stringCheck,
1243
- number: number,
1244
- numberCheck: numberCheck,
1245
- arrayCheck: arrayCheck,
1246
- switchStructureCheck: switchStructureCheck
1296
+ switchStructureCheck: switchStructureCheck,
1297
+ uint16: uint16,
1298
+ uint32: uint32,
1299
+ uint64: uint64,
1300
+ uint8: uint8,
1301
+ utf8Read: utf8Read
1247
1302
  });
1248
1303
 
1249
1304
  const DEFINITION_MISMATCH = -1;
1250
1305
  function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
1251
- const $root = decoder.$root;
1306
+ const $root = decoder.root;
1252
1307
  const previousValue = ref[$getByIndex](index);
1253
1308
  let value;
1254
1309
  if ((operation & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
@@ -1434,7 +1489,8 @@
1434
1489
  };
1435
1490
  const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1436
1491
  // "uncompressed" index + operation (array/map items)
1437
- const operation = bytes[it.offset++];
1492
+ let operation = bytes[it.offset++];
1493
+ let index;
1438
1494
  if (operation === exports.OPERATION.CLEAR) {
1439
1495
  //
1440
1496
  // When decoding:
@@ -1448,8 +1504,8 @@
1448
1504
  else if (operation === exports.OPERATION.DELETE_BY_REFID) {
1449
1505
  // TODO: refactor here, try to follow same flow as below
1450
1506
  const refId = number(bytes, it);
1451
- const previousValue = decoder.$root.refs.get(refId);
1452
- const index = ref.findIndex((value) => value === previousValue);
1507
+ const previousValue = decoder.root.refs.get(refId);
1508
+ index = ref.findIndex((value) => value === previousValue);
1453
1509
  ref[$deleteByIndex](index);
1454
1510
  allChanges.push({
1455
1511
  ref,
@@ -1462,7 +1518,18 @@
1462
1518
  });
1463
1519
  return;
1464
1520
  }
1465
- const index = number(bytes, it);
1521
+ else if (operation === exports.OPERATION.ADD_BY_REFID) {
1522
+ // operation = OPERATION.ADD;
1523
+ const refId = number(bytes, it);
1524
+ const itemByRefId = decoder.root.refs.get(refId);
1525
+ // use existing index, or push new value
1526
+ index = (itemByRefId)
1527
+ ? ref.findIndex((value) => value === itemByRefId)
1528
+ : ref.length;
1529
+ }
1530
+ else {
1531
+ index = number(bytes, it);
1532
+ }
1466
1533
  const type = ref[$childType];
1467
1534
  let dynamicIndex = index;
1468
1535
  const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
@@ -1871,14 +1938,6 @@
1871
1938
  lastIndexOf(searchElement, fromIndex = this.length - 1) {
1872
1939
  return this.items.lastIndexOf(searchElement, fromIndex);
1873
1940
  }
1874
- /**
1875
- * Determines whether all the members of an array satisfy the specified test.
1876
- * @param callbackfn A function that accepts up to three arguments. The every method calls
1877
- * the callbackfn function for each element in the array until the callbackfn returns a value
1878
- * which is coercible to the Boolean value false, or until the end of the array.
1879
- * @param thisArg An object to which the this keyword can refer in the callbackfn function.
1880
- * If thisArg is omitted, undefined is used as the this value.
1881
- */
1882
1941
  every(callbackfn, thisArg) {
1883
1942
  return this.items.every(callbackfn, thisArg);
1884
1943
  }
@@ -2557,6 +2616,7 @@
2557
2616
  const constructor = target.constructor;
2558
2617
  const parentClass = Object.getPrototypeOf(constructor);
2559
2618
  const parentMetadata = parentClass[Symbol.metadata];
2619
+ // TODO: use Metadata.initialize()
2560
2620
  const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
2561
2621
  if (!metadata[fieldName]) {
2562
2622
  //
@@ -2581,8 +2641,8 @@
2581
2641
  // for inheritance support
2582
2642
  TypeContext.register(constructor);
2583
2643
  const parentClass = Object.getPrototypeOf(constructor);
2584
- const parentMetadata = parentClass[Symbol.metadata];
2585
- const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
2644
+ const parentMetadata = parentClass && parentClass[Symbol.metadata];
2645
+ const metadata = Metadata.initialize(constructor, parentMetadata);
2586
2646
  let fieldIndex;
2587
2647
  /**
2588
2648
  * skip if descriptor already exists for this field (`@deprecated()`)
@@ -3355,6 +3415,8 @@
3355
3415
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
3356
3416
  PERFORMANCE OF THIS SOFTWARE.
3357
3417
  ***************************************************************************** */
3418
+ /* global Reflect, Promise, SuppressedError, Symbol */
3419
+
3358
3420
 
3359
3421
  function __decorate(decorators, target, key, desc) {
3360
3422
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -3386,11 +3448,14 @@
3386
3448
  setRoot(state) {
3387
3449
  this.root = new Root();
3388
3450
  this.state = state;
3451
+ // Workaround to allow using an empty Schema.
3452
+ if (state.constructor[Symbol.metadata] === undefined) {
3453
+ Metadata.init(state);
3454
+ }
3389
3455
  state[$changes].setRoot(this.root);
3390
3456
  }
3391
- encode(it = { offset: 0 }, view, bytes = this.sharedBuffer, changeTrees = this.root.changes) {
3457
+ encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees) {
3392
3458
  const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
3393
- const isEncodeAll = this.root.allChanges === changeTrees;
3394
3459
  const hasView = (view !== undefined);
3395
3460
  const rootChangeTree = this.state[$changes];
3396
3461
  const changeTreesIterator = changeTrees.entries();
@@ -3399,6 +3464,12 @@
3399
3464
  const ctor = ref['constructor'];
3400
3465
  const encoder = ctor[$encoder];
3401
3466
  const filter = ctor[$filter];
3467
+ // try { throw new Error(); } catch (e) {
3468
+ // // only print if not coming from Reflection.ts
3469
+ // if (!e.stack.includes("src/Reflection.ts")) {
3470
+ // console.log("ChangeTree:", { ref: ref.constructor.name, });
3471
+ // }
3472
+ // }
3402
3473
  if (hasView) {
3403
3474
  if (!view.items.has(changeTree)) {
3404
3475
  view.invisible.add(changeTree);
@@ -3410,8 +3481,8 @@
3410
3481
  }
3411
3482
  // skip root `refId` if it's the first change tree
3412
3483
  if (it.offset !== initialOffset || changeTree !== rootChangeTree) {
3413
- bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3414
- number$1(bytes, changeTree.refId, it);
3484
+ buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3485
+ number$1(buffer, changeTree.refId, it);
3415
3486
  }
3416
3487
  const changesIterator = changes.entries();
3417
3488
  for (const [fieldIndex, operation] of changesIterator) {
@@ -3428,22 +3499,35 @@
3428
3499
  // view?.invisible.add(changeTree);
3429
3500
  continue;
3430
3501
  }
3431
- // console.log("WILL ENCODE", {
3432
- // ref: changeTree.ref.constructor.name,
3433
- // fieldIndex,
3434
- // operation: OPERATION[operation],
3435
- // });
3436
- encoder(this, bytes, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3502
+ // try { throw new Error(); } catch (e) {
3503
+ // // only print if not coming from Reflection.ts
3504
+ // if (!e.stack.includes("src/Reflection.ts")) {
3505
+ // console.log("WILL ENCODE", {
3506
+ // ref: changeTree.ref.constructor.name,
3507
+ // fieldIndex,
3508
+ // operation: OPERATION[operation],
3509
+ // });
3510
+ // }
3511
+ // }
3512
+ encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3437
3513
  }
3438
3514
  }
3439
- if (it.offset > bytes.byteLength) {
3440
- const newSize = getNextPowerOf2(this.sharedBuffer.byteLength * 2);
3441
- console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + bytes.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
3515
+ if (it.offset > buffer.byteLength) {
3516
+ const newSize = getNextPowerOf2(buffer.byteLength * 2);
3517
+ console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
3518
+
3519
+ import { Encoder } from "@colyseus/schema";
3520
+ Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
3521
+ `);
3442
3522
  //
3443
3523
  // resize buffer and re-encode (TODO: can we avoid re-encoding here?)
3444
3524
  //
3445
- this.sharedBuffer = Buffer.allocUnsafeSlow(newSize);
3446
- return this.encode({ offset: initialOffset }, view);
3525
+ buffer = Buffer.allocUnsafeSlow(newSize);
3526
+ // assign resized buffer to local sharedBuffer
3527
+ if (buffer === this.sharedBuffer) {
3528
+ this.sharedBuffer = buffer;
3529
+ }
3530
+ return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
3447
3531
  }
3448
3532
  else {
3449
3533
  //
@@ -3455,38 +3539,37 @@
3455
3539
  //
3456
3540
  this.onEndEncode(changeTrees);
3457
3541
  }
3458
- // return bytes;
3459
- return bytes.slice(0, it.offset);
3542
+ return buffer.subarray(0, it.offset);
3460
3543
  }
3461
3544
  }
3462
- encodeAll(it = { offset: 0 }) {
3463
- // console.log(`encodeAll(), this.$root.allChanges (${this.$root.allChanges.size})`);
3464
- // Array.from(this.$root.allChanges.entries()).map((item) => {
3465
- // console.log("->", item[0].refId, item[0].ref.toJSON());
3545
+ encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
3546
+ // console.log(`encodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
3547
+ // Array.from(this.root.allChanges.entries()).map((item) => {
3548
+ // console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
3466
3549
  // });
3467
- return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
3550
+ return this.encode(it, undefined, buffer, this.root.allChanges, true);
3468
3551
  }
3469
3552
  encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3470
3553
  const viewOffset = it.offset;
3471
- // console.log(`encodeAllView(), this.$root.allFilteredChanges (${this.$root.allFilteredChanges.size})`);
3554
+ // console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
3472
3555
  // this.debugAllFilteredChanges();
3473
3556
  // try to encode "filtered" changes
3474
- this.encode(it, view, bytes, this.root.allFilteredChanges);
3557
+ this.encode(it, view, bytes, this.root.allFilteredChanges, true);
3475
3558
  return Buffer.concat([
3476
- bytes.slice(0, sharedOffset),
3477
- bytes.slice(viewOffset, it.offset)
3559
+ bytes.subarray(0, sharedOffset),
3560
+ bytes.subarray(viewOffset, it.offset)
3478
3561
  ]);
3479
3562
  }
3480
- // debugAllFilteredChanges() {
3481
- // Array.from(this.$root.allFilteredChanges.entries()).map((item) => {
3482
- // console.log("->", { refId: item[0].refId }, item[0].ref.toJSON());
3483
- // if (Array.isArray(item[0].ref.toJSON())) {
3484
- // item[1].forEach((op, key) => {
3485
- // console.log(" ->", { key, op: OPERATION[op] });
3486
- // })
3487
- // }
3488
- // });
3489
- // }
3563
+ debugAllFilteredChanges() {
3564
+ Array.from(this.root.allFilteredChanges.entries()).map((item) => {
3565
+ console.log("->", { refId: item[0].refId, changes: item[1].size }, item[0].ref.toJSON());
3566
+ if (Array.isArray(item[0].ref.toJSON())) {
3567
+ item[1].forEach((op, key) => {
3568
+ console.log(" ->", { key, op: exports.OPERATION[op] });
3569
+ });
3570
+ }
3571
+ });
3572
+ }
3490
3573
  encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3491
3574
  const viewOffset = it.offset;
3492
3575
  // try to encode "filtered" changes
@@ -3518,8 +3601,8 @@
3518
3601
  // clear "view" changes after encoding
3519
3602
  view.changes.clear();
3520
3603
  return Buffer.concat([
3521
- bytes.slice(0, sharedOffset),
3522
- bytes.slice(viewOffset, it.offset)
3604
+ bytes.subarray(0, sharedOffset),
3605
+ bytes.subarray(viewOffset, it.offset)
3523
3606
  ]);
3524
3607
  }
3525
3608
  onEndEncode(changeTrees = this.root.changes) {
@@ -3698,12 +3781,12 @@
3698
3781
  }
3699
3782
  setRoot(root) {
3700
3783
  this.state = root;
3701
- this.$root = new ReferenceTracker();
3702
- this.$root.addRef(0, root);
3784
+ this.root = new ReferenceTracker();
3785
+ this.root.addRef(0, root);
3703
3786
  }
3704
3787
  decode(bytes, it = { offset: 0 }, ref = this.state) {
3705
3788
  const allChanges = [];
3706
- const $root = this.$root;
3789
+ const $root = this.root;
3707
3790
  const totalBytes = bytes.byteLength;
3708
3791
  let decoder = ref['constructor'][$decoder];
3709
3792
  this.currentRefId = 0;
@@ -3784,7 +3867,7 @@
3784
3867
  previousValue: value
3785
3868
  });
3786
3869
  if (needRemoveRef) {
3787
- this.$root.removeRef(this.$root.refIds.get(value));
3870
+ this.root.removeRef(this.root.refIds.get(value));
3788
3871
  }
3789
3872
  });
3790
3873
  }
@@ -3824,7 +3907,7 @@
3824
3907
  super(...arguments);
3825
3908
  this.types = new ArraySchema();
3826
3909
  }
3827
- static encode(instance, context) {
3910
+ static encode(instance, context, it = { offset: 0 }) {
3828
3911
  if (!context) {
3829
3912
  context = new TypeContext(instance.constructor);
3830
3913
  }
@@ -3881,7 +3964,6 @@
3881
3964
  }
3882
3965
  buildType(type, klass[Symbol.metadata]);
3883
3966
  }
3884
- const it = { offset: 0 };
3885
3967
  const buf = encoder.encodeAll(it);
3886
3968
  return Buffer.from(buf, 0, it.offset);
3887
3969
  }
@@ -3889,59 +3971,298 @@
3889
3971
  const reflection = new Reflection();
3890
3972
  const reflectionDecoder = new Decoder(reflection);
3891
3973
  reflectionDecoder.decode(bytes, it);
3892
- const context = new TypeContext();
3893
- const schemaTypes = reflection.types.reduce((types, reflectionType) => {
3894
- const parentKlass = types[reflectionType.extendsId] || Schema;
3895
- const schema = class _ extends parentKlass {
3974
+ const typeContext = new TypeContext();
3975
+ // 1st pass, initialize metadata + inheritance
3976
+ reflection.types.forEach((reflectionType) => {
3977
+ const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
3978
+ const schema = class _ extends parentClass {
3896
3979
  };
3897
- // const _metadata = Object.create(_classSuper[Symbol.metadata] ?? null);
3898
- const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
3899
- Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
3980
+ const parentMetadata = parentClass[Symbol.metadata];
3900
3981
  // register for inheritance support
3901
3982
  TypeContext.register(schema);
3902
- const typeid = reflectionType.id;
3903
- types[typeid] = schema;
3904
- context.add(schema, typeid);
3905
- return types;
3983
+ // for inheritance support
3984
+ Metadata.initialize(schema, parentMetadata);
3985
+ typeContext.add(schema, reflectionType.id);
3906
3986
  }, {});
3987
+ // 2nd pass, set fields
3907
3988
  reflection.types.forEach((reflectionType) => {
3908
- const schemaType = schemaTypes[reflectionType.id];
3989
+ const schemaType = typeContext.get(reflectionType.id);
3909
3990
  const metadata = schemaType[Symbol.metadata];
3910
- const parentKlass = reflection.types[reflectionType.extendsId];
3911
- const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
3991
+ // FIXME: use metadata[-1] to get field count
3992
+ const parentFieldIndex = 0;
3993
+ // console.log("--------------------");
3994
+ // // console.log("reflectionType", reflectionType.toJSON());
3995
+ // console.log("reflectionType.fields", reflectionType.fields.toJSON());
3996
+ // console.log("parentFieldIndex", parentFieldIndex);
3997
+ //
3998
+ // FIXME: set fields using parentKlass as well
3999
+ // currently the fields are duplicated on inherited classes
4000
+ //
4001
+ // // const parentKlass = reflection.types[reflectionType.extendsId];
4002
+ // // parentKlass.fields
3912
4003
  reflectionType.fields.forEach((field, i) => {
3913
4004
  const fieldIndex = parentFieldIndex + i;
3914
4005
  if (field.referencedType !== undefined) {
3915
4006
  let fieldType = field.type;
3916
- let refType = schemaTypes[field.referencedType];
4007
+ let refType = typeContext.get(field.referencedType);
3917
4008
  // map or array of primitive type (-1)
3918
4009
  if (!refType) {
3919
4010
  const typeInfo = field.type.split(":");
3920
4011
  fieldType = typeInfo[0];
3921
- refType = typeInfo[1];
4012
+ refType = typeInfo[1]; // string
3922
4013
  }
3923
4014
  if (fieldType === "ref") {
3924
- // type(refType)(schemaType.prototype, field.name);
3925
4015
  Metadata.addField(metadata, fieldIndex, field.name, refType);
3926
4016
  }
3927
4017
  else {
3928
- // type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
3929
4018
  Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
3930
4019
  }
3931
4020
  }
3932
4021
  else {
3933
- // type(field.type as PrimitiveType)(schemaType.prototype, field.name);
3934
4022
  Metadata.addField(metadata, fieldIndex, field.name, field.type);
3935
4023
  }
3936
4024
  });
3937
4025
  });
3938
- return new (schemaTypes[0])();
4026
+ // @ts-ignore
4027
+ return new (typeContext.get(0))();
3939
4028
  }
3940
4029
  }
3941
4030
  __decorate([
3942
4031
  type([ReflectionType])
3943
4032
  ], Reflection.prototype, "types", void 0);
3944
4033
 
4034
+ function getDecoderStateCallbacks(decoder) {
4035
+ const $root = decoder.root;
4036
+ const callbacks = $root.callbacks;
4037
+ let isTriggeringOnAdd = false;
4038
+ decoder.triggerChanges = function (allChanges) {
4039
+ const uniqueRefIds = new Set();
4040
+ for (let i = 0, l = allChanges.length; i < l; i++) {
4041
+ const change = allChanges[i];
4042
+ const refId = change.refId;
4043
+ const ref = change.ref;
4044
+ const $callbacks = callbacks[refId];
4045
+ if (!$callbacks) {
4046
+ continue;
4047
+ }
4048
+ //
4049
+ // trigger onRemove on child structure.
4050
+ //
4051
+ if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE &&
4052
+ change.previousValue instanceof Schema) {
4053
+ const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[exports.OPERATION.DELETE];
4054
+ for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
4055
+ deleteCallbacks[i]();
4056
+ }
4057
+ }
4058
+ if (ref instanceof Schema) {
4059
+ //
4060
+ // Handle schema instance
4061
+ //
4062
+ if (!uniqueRefIds.has(refId)) {
4063
+ // trigger onChange
4064
+ const replaceCallbacks = $callbacks?.[exports.OPERATION.REPLACE];
4065
+ for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
4066
+ replaceCallbacks[i]();
4067
+ // try {
4068
+ // } catch (e) {
4069
+ // console.error(e);
4070
+ // }
4071
+ }
4072
+ }
4073
+ if ($callbacks.hasOwnProperty(change.field)) {
4074
+ const fieldCallbacks = $callbacks[change.field];
4075
+ for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
4076
+ fieldCallbacks[i](change.value, change.previousValue);
4077
+ // try {
4078
+ // } catch (e) {
4079
+ // console.error(e);
4080
+ // }
4081
+ }
4082
+ }
4083
+ }
4084
+ else {
4085
+ //
4086
+ // Handle collection of items
4087
+ //
4088
+ if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
4089
+ //
4090
+ // FIXME: `previousValue` should always be available.
4091
+ //
4092
+ if (change.previousValue !== undefined) {
4093
+ // triger onRemove
4094
+ const deleteCallbacks = $callbacks[exports.OPERATION.DELETE];
4095
+ for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
4096
+ deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
4097
+ }
4098
+ }
4099
+ // Handle DELETE_AND_ADD operations
4100
+ // FIXME: should we set "isTriggeringOnAdd" here?
4101
+ if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD) {
4102
+ const addCallbacks = $callbacks[exports.OPERATION.ADD];
4103
+ for (let i = addCallbacks?.length - 1; i >= 0; i--) {
4104
+ addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4105
+ }
4106
+ }
4107
+ }
4108
+ else if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD && change.previousValue === undefined) {
4109
+ // triger onAdd
4110
+ isTriggeringOnAdd = true;
4111
+ const addCallbacks = $callbacks[exports.OPERATION.ADD];
4112
+ for (let i = addCallbacks?.length - 1; i >= 0; i--) {
4113
+ addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4114
+ }
4115
+ isTriggeringOnAdd = false;
4116
+ }
4117
+ // trigger onChange
4118
+ if (change.value !== change.previousValue) {
4119
+ const replaceCallbacks = $callbacks[exports.OPERATION.REPLACE];
4120
+ for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
4121
+ replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4122
+ }
4123
+ }
4124
+ }
4125
+ uniqueRefIds.add(refId);
4126
+ }
4127
+ };
4128
+ function getProxy(metadataOrType, context) {
4129
+ let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
4130
+ let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
4131
+ (metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
4132
+ if (metadata && !isCollection) {
4133
+ const onAdd = function (ref, prop, callback, immediate) {
4134
+ // immediate trigger
4135
+ if (immediate &&
4136
+ context.instance[prop] !== undefined &&
4137
+ !isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
4138
+ ) {
4139
+ callback(context.instance[prop], undefined);
4140
+ }
4141
+ return $root.addCallback($root.refIds.get(ref), prop, callback);
4142
+ };
4143
+ /**
4144
+ * Schema instances
4145
+ */
4146
+ return new Proxy({
4147
+ listen: function listen(prop, callback, immediate = true) {
4148
+ if (context.instance) {
4149
+ return onAdd(context.instance, prop, callback, immediate);
4150
+ }
4151
+ else {
4152
+ // collection instance not received yet
4153
+ context.onInstanceAvailable((ref, existing) => onAdd(ref, prop, callback, immediate && existing));
4154
+ }
4155
+ },
4156
+ onChange: function onChange(callback) {
4157
+ return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, callback);
4158
+ },
4159
+ bindTo: function bindTo(targetObject, properties) {
4160
+ //
4161
+ // TODO: refactor this implementation. There is room for improvement here.
4162
+ //
4163
+ if (!properties) {
4164
+ properties = Object.keys(metadata);
4165
+ }
4166
+ return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, () => {
4167
+ properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
4168
+ });
4169
+ }
4170
+ }, {
4171
+ get(target, prop) {
4172
+ if (metadata[prop]) {
4173
+ const instance = context.instance?.[prop];
4174
+ const onInstanceAvailable = ((callback) => {
4175
+ const unbind = $(context.instance).listen(prop, (value, _) => {
4176
+ callback(value, false);
4177
+ // FIXME: by "unbinding" the callback here,
4178
+ // it will not support when the server
4179
+ // re-instantiates the instance.
4180
+ //
4181
+ unbind?.();
4182
+ }, false);
4183
+ // has existing value
4184
+ if ($root.refIds.get(instance) !== undefined) {
4185
+ callback(instance, true);
4186
+ }
4187
+ });
4188
+ return getProxy(metadata[prop].type, {
4189
+ instance,
4190
+ parentInstance: context.instance,
4191
+ onInstanceAvailable,
4192
+ });
4193
+ }
4194
+ else {
4195
+ // accessing the function
4196
+ return target[prop];
4197
+ }
4198
+ },
4199
+ has(target, prop) { return metadata[prop] !== undefined; },
4200
+ set(_, _1, _2) { throw new Error("not allowed"); },
4201
+ deleteProperty(_, _1) { throw new Error("not allowed"); },
4202
+ });
4203
+ }
4204
+ else {
4205
+ /**
4206
+ * Collection instances
4207
+ */
4208
+ const onAdd = function (ref, callback, immediate) {
4209
+ // Trigger callback on existing items
4210
+ if (immediate) {
4211
+ ref.forEach((v, k) => callback(v, k));
4212
+ }
4213
+ return $root.addCallback($root.refIds.get(ref), exports.OPERATION.ADD, callback);
4214
+ };
4215
+ const onRemove = function (ref, callback) {
4216
+ return $root.addCallback($root.refIds.get(ref), exports.OPERATION.DELETE, callback);
4217
+ };
4218
+ return new Proxy({
4219
+ onAdd: function (callback, immediate = true) {
4220
+ //
4221
+ // https://github.com/colyseus/schema/issues/147
4222
+ // If parent instance has "onAdd" registered, avoid triggering immediate callback.
4223
+ //
4224
+ // FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
4225
+ //
4226
+ if (context.onInstanceAvailable) {
4227
+ // collection instance not received yet
4228
+ context.onInstanceAvailable((ref, existing) => onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
4229
+ }
4230
+ else if (context.instance) {
4231
+ onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
4232
+ }
4233
+ },
4234
+ onRemove: function (callback) {
4235
+ if (context.onInstanceAvailable) {
4236
+ // collection instance not received yet
4237
+ context.onInstanceAvailable((ref) => onRemove(ref, callback));
4238
+ }
4239
+ else if (context.instance) {
4240
+ onRemove(context.instance, callback);
4241
+ }
4242
+ },
4243
+ }, {
4244
+ get(target, prop) {
4245
+ if (!target[prop]) {
4246
+ throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
4247
+ }
4248
+ return target[prop];
4249
+ },
4250
+ has(target, prop) { return target[prop] !== undefined; },
4251
+ set(_, _1, _2) { throw new Error("not allowed"); },
4252
+ deleteProperty(_, _1) { throw new Error("not allowed"); },
4253
+ });
4254
+ }
4255
+ }
4256
+ function $(instance) {
4257
+ return getProxy(undefined, { instance });
4258
+ }
4259
+ return $;
4260
+ }
4261
+
4262
+ function getRawChangesCallback(decoder, callback) {
4263
+ decoder.triggerChanges = callback;
4264
+ }
4265
+
3945
4266
  class StateView {
3946
4267
  constructor() {
3947
4268
  /**
@@ -3964,12 +4285,18 @@
3964
4285
  console.warn("StateView#add(), invalid object:", obj);
3965
4286
  return this;
3966
4287
  }
4288
+ // FIXME: ArraySchema/MapSchema does not have metadata
4289
+ const metadata = obj.constructor[Symbol.metadata];
3967
4290
  let changeTree = obj[$changes];
3968
4291
  this.items.add(changeTree);
3969
4292
  // Add children of this ChangeTree to this view
3970
- changeTree.forEachChild((change, _) => this.add(change.ref, tag));
3971
- // FIXME: ArraySchema/MapSchema does not have metadata
3972
- const metadata = obj.constructor[Symbol.metadata];
4293
+ changeTree.forEachChild((change, index) => {
4294
+ // Do not ADD children that don't have the same tag
4295
+ if (metadata && metadata[metadata[index]].tag !== tag) {
4296
+ return;
4297
+ }
4298
+ this.add(change.ref, tag);
4299
+ });
3973
4300
  // add parent ChangeTree's, if they are invisible to this view
3974
4301
  // TODO: REFACTOR addParent()
3975
4302
  this.addParent(changeTree, tag);
@@ -3996,7 +4323,6 @@
3996
4323
  tags = this.tags.get(changeTree);
3997
4324
  }
3998
4325
  tags.add(tag);
3999
- // console.log("BY TAG:", tag);
4000
4326
  // Ref: add tagged properties
4001
4327
  metadata?.[-3]?.[tag]?.forEach((index) => {
4002
4328
  if (changeTree.getChange(index) !== exports.OPERATION.DELETE) {
@@ -4166,10 +4492,10 @@
4166
4492
  exports.encode = encode;
4167
4493
  exports.encodeKeyValueOperation = encodeArray;
4168
4494
  exports.encodeSchemaOperation = encodeSchemaOperation;
4495
+ exports.getDecoderStateCallbacks = getDecoderStateCallbacks;
4496
+ exports.getRawChangesCallback = getRawChangesCallback;
4169
4497
  exports.registerType = registerType;
4170
4498
  exports.type = type;
4171
4499
  exports.view = view;
4172
4500
 
4173
- Object.defineProperty(exports, '__esModule', { value: true });
4174
-
4175
4501
  }));