@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
@@ -23,6 +23,7 @@ var OPERATION;
23
23
  OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
24
24
  OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
25
25
  OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
26
+ OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
26
27
  })(OPERATION || (OPERATION = {}));
27
28
 
28
29
  Symbol.metadata ??= Symbol.for("Symbol.metadata");
@@ -139,6 +140,45 @@ const Metadata = {
139
140
  isDeprecated(metadata, field) {
140
141
  return metadata[field].deprecated === true;
141
142
  },
143
+ init(klass) {
144
+ //
145
+ // Used only to initialize an empty Schema (Encoder#constructor)
146
+ // TODO: remove/refactor this...
147
+ //
148
+ const metadata = {};
149
+ klass.constructor[Symbol.metadata] = metadata;
150
+ Object.defineProperty(metadata, -1, {
151
+ value: 0,
152
+ enumerable: false,
153
+ configurable: true,
154
+ });
155
+ },
156
+ initialize(constructor, parentMetadata) {
157
+ let metadata = constructor[Symbol.metadata] ?? Object.create(null);
158
+ // make sure inherited classes have their own metadata object.
159
+ if (constructor[Symbol.metadata] === parentMetadata) {
160
+ metadata = Object.create(null);
161
+ if (parentMetadata) {
162
+ // assign parent metadata to current
163
+ Object.assign(metadata, parentMetadata);
164
+ for (let i = 0; i <= parentMetadata[-1]; i++) {
165
+ Object.defineProperty(metadata, i, {
166
+ value: parentMetadata[i],
167
+ enumerable: false,
168
+ configurable: true,
169
+ });
170
+ }
171
+ Object.defineProperty(metadata, -1, {
172
+ value: parentMetadata[-1],
173
+ enumerable: false,
174
+ configurable: true,
175
+ writable: true,
176
+ });
177
+ }
178
+ }
179
+ constructor[Symbol.metadata] = metadata;
180
+ return metadata;
181
+ },
142
182
  isValidInstance(klass) {
143
183
  return (klass.constructor[Symbol.metadata] &&
144
184
  Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], -1));
@@ -258,14 +298,12 @@ class ChangeTree {
258
298
  this.checkIsFiltered(parent, parentIndex);
259
299
  if (!this.isFiltered) {
260
300
  this.root.changes.set(this, this.changes);
301
+ this.root.allChanges.set(this, this.allChanges);
261
302
  }
262
303
  if (this.isFiltered || this.isPartiallyFiltered) {
263
304
  this.root.filteredChanges.set(this, this.filteredChanges);
264
305
  this.root.allFilteredChanges.set(this, this.filteredChanges);
265
306
  }
266
- else {
267
- this.root.allChanges.set(this, this.allChanges);
268
- }
269
307
  this.ensureRefId();
270
308
  this.forEachChild((changeTree, atIndex) => {
271
309
  changeTree.setParent(this.ref, root, atIndex);
@@ -357,7 +395,6 @@ class ChangeTree {
357
395
  }
358
396
  _shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
359
397
  Array.from(allChangeSet.entries()).forEach(([index, op]) => {
360
- // console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
361
398
  if (index >= startIndex) {
362
399
  allChangeSet.delete(index);
363
400
  allChangeSet.set(index + shiftIndex, op);
@@ -498,7 +535,7 @@ class ChangeTree {
498
535
  }
499
536
  checkIsFiltered(parent, parentIndex) {
500
537
  // Detect if current structure has "filters" declared
501
- this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
538
+ this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
502
539
  // TODO: support "partially filtered", where the instance is visible, but only a field is not.
503
540
  // Detect if parent has "filters" declared
504
541
  while (parent && !this.isFiltered) {
@@ -559,26 +596,29 @@ try {
559
596
  textEncoder = new TextEncoder();
560
597
  }
561
598
  catch (e) { }
562
- function utf8Length(str) {
563
- var c = 0, length = 0;
564
- for (var i = 0, l = str.length; i < l; i++) {
565
- c = str.charCodeAt(i);
566
- if (c < 0x80) {
567
- length += 1;
568
- }
569
- else if (c < 0x800) {
570
- length += 2;
571
- }
572
- else if (c < 0xd800 || c >= 0xe000) {
573
- length += 3;
574
- }
575
- else {
576
- i++;
577
- length += 4;
599
+ const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
600
+ const utf8Length = (hasBufferByteLength)
601
+ ? Buffer.byteLength // node
602
+ : function (str, _) {
603
+ var c = 0, length = 0;
604
+ for (var i = 0, l = str.length; i < l; i++) {
605
+ c = str.charCodeAt(i);
606
+ if (c < 0x80) {
607
+ length += 1;
608
+ }
609
+ else if (c < 0x800) {
610
+ length += 2;
611
+ }
612
+ else if (c < 0xd800 || c >= 0xe000) {
613
+ length += 3;
614
+ }
615
+ else {
616
+ i++;
617
+ length += 4;
618
+ }
578
619
  }
579
- }
580
- return length;
581
- }
620
+ return length;
621
+ };
582
622
  function utf8Write(view, str, it) {
583
623
  var c = 0;
584
624
  for (var i = 0, l = str.length; i < l; i++) {
@@ -673,8 +713,7 @@ function string$1(bytes, value, it) {
673
713
  if (!value) {
674
714
  value = "";
675
715
  }
676
- // let length = utf8Length(value);
677
- let length = Buffer.byteLength(value, "utf8");
716
+ let length = utf8Length(value, "utf8");
678
717
  let size = 0;
679
718
  // fixstr
680
719
  if (length < 0x20) {
@@ -785,23 +824,23 @@ function number$1(bytes, value, it) {
785
824
 
786
825
  var encode = /*#__PURE__*/Object.freeze({
787
826
  __proto__: null,
788
- utf8Length: utf8Length,
789
- utf8Write: utf8Write,
790
- int8: int8$1,
791
- uint8: uint8$1,
827
+ boolean: boolean$1,
828
+ float32: float32$1,
829
+ float64: float64$1,
792
830
  int16: int16$1,
793
- uint16: uint16$1,
794
831
  int32: int32$1,
795
- uint32: uint32$1,
796
832
  int64: int64$1,
833
+ int8: int8$1,
834
+ number: number$1,
835
+ string: string$1,
836
+ uint16: uint16$1,
837
+ uint32: uint32$1,
797
838
  uint64: uint64$1,
798
- float32: float32$1,
799
- float64: float64$1,
839
+ uint8: uint8$1,
840
+ utf8Length: utf8Length,
841
+ utf8Write: utf8Write,
800
842
  writeFloat32: writeFloat32,
801
- writeFloat64: writeFloat64,
802
- boolean: boolean$1,
803
- string: string$1,
804
- number: number$1
843
+ writeFloat64: writeFloat64
805
844
  });
806
845
 
807
846
  class EncodeSchemaError extends Error {
@@ -943,6 +982,18 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
943
982
  }
944
983
  const type = changeTree.getType(field);
945
984
  const value = changeTree.getValue(field);
985
+ // try { throw new Error(); } catch (e) {
986
+ // // only print if not coming from Reflection.ts
987
+ // if (!e.stack.includes("src/Reflection.ts")) {
988
+ // console.log("encodeKeyValueOperation -> ", {
989
+ // ref: changeTree.ref.constructor.name,
990
+ // field,
991
+ // operation: OPERATION[operation],
992
+ // value: value?.toJSON(),
993
+ // items: ref.toJSON(),
994
+ // });
995
+ // }
996
+ // }
946
997
  // TODO: inline this function call small performance gain
947
998
  encodeValue(encoder, bytes, ref, type, value, field, operation, it);
948
999
  };
@@ -952,15 +1003,19 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
952
1003
  */
953
1004
  const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
954
1005
  const ref = changeTree.ref;
955
- if (hasView &&
956
- operation === OPERATION.DELETE &&
957
- typeof (changeTree.getType(field)) !== "string") {
958
- // encode delete by refId (array of schemas)
959
- bytes[it.offset++] = OPERATION.DELETE_BY_REFID;
960
- const value = ref['tmpItems'][field];
961
- const refId = value[$changes].refId;
962
- number$1(bytes, refId, it);
963
- return;
1006
+ const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
1007
+ let refOrIndex;
1008
+ if (useOperationByRefId) {
1009
+ refOrIndex = ref['tmpItems'][field][$changes].refId;
1010
+ if (operation === OPERATION.DELETE) {
1011
+ operation = OPERATION.DELETE_BY_REFID;
1012
+ }
1013
+ else if (operation === OPERATION.ADD) {
1014
+ operation = OPERATION.ADD_BY_REFID;
1015
+ }
1016
+ }
1017
+ else {
1018
+ refOrIndex = field;
964
1019
  }
965
1020
  // encode operation
966
1021
  bytes[it.offset++] = operation & 255;
@@ -969,7 +1024,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
969
1024
  return;
970
1025
  }
971
1026
  // encode index
972
- number$1(bytes, field, it);
1027
+ number$1(bytes, refOrIndex, it);
973
1028
  // Do not encode value for DELETE operations
974
1029
  if (operation === OPERATION.DELETE) {
975
1030
  return;
@@ -1009,9 +1064,9 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
1009
1064
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1010
1065
  * SOFTWARE
1011
1066
  */
1012
- function utf8Read(bytes, offset, length) {
1067
+ function utf8Read(bytes, it, length) {
1013
1068
  var string = '', chr = 0;
1014
- for (var i = offset, end = offset + length; i < end; i++) {
1069
+ for (var i = it.offset, end = it.offset + length; i < end; i++) {
1015
1070
  var byte = bytes[i];
1016
1071
  if ((byte & 0x80) === 0x00) {
1017
1072
  string += String.fromCharCode(byte);
@@ -1046,6 +1101,7 @@ function utf8Read(bytes, offset, length) {
1046
1101
  // (do not throw error to avoid server/client from crashing due to hack attemps)
1047
1102
  // throw new Error('Invalid byte ' + byte.toString(16));
1048
1103
  }
1104
+ it.offset += length;
1049
1105
  return string;
1050
1106
  }
1051
1107
  function int8(bytes, it) {
@@ -1113,9 +1169,7 @@ function string(bytes, it) {
1113
1169
  else if (prefix === 0xdb) {
1114
1170
  length = uint32(bytes, it);
1115
1171
  }
1116
- const value = utf8Read(bytes, it.offset, length);
1117
- it.offset += length;
1118
- return value;
1172
+ return utf8Read(bytes, it, length);
1119
1173
  }
1120
1174
  function stringCheck(bytes, it) {
1121
1175
  const prefix = bytes[it.offset];
@@ -1219,30 +1273,31 @@ function switchStructureCheck(bytes, it) {
1219
1273
 
1220
1274
  var decode = /*#__PURE__*/Object.freeze({
1221
1275
  __proto__: null,
1222
- int8: int8,
1223
- uint8: uint8,
1224
- int16: int16,
1225
- uint16: uint16,
1226
- int32: int32,
1227
- uint32: uint32,
1276
+ arrayCheck: arrayCheck,
1277
+ boolean: boolean,
1228
1278
  float32: float32,
1229
1279
  float64: float64,
1280
+ int16: int16,
1281
+ int32: int32,
1230
1282
  int64: int64,
1231
- uint64: uint64,
1283
+ int8: int8,
1284
+ number: number,
1285
+ numberCheck: numberCheck,
1232
1286
  readFloat32: readFloat32,
1233
1287
  readFloat64: readFloat64,
1234
- boolean: boolean,
1235
1288
  string: string,
1236
1289
  stringCheck: stringCheck,
1237
- number: number,
1238
- numberCheck: numberCheck,
1239
- arrayCheck: arrayCheck,
1240
- switchStructureCheck: switchStructureCheck
1290
+ switchStructureCheck: switchStructureCheck,
1291
+ uint16: uint16,
1292
+ uint32: uint32,
1293
+ uint64: uint64,
1294
+ uint8: uint8,
1295
+ utf8Read: utf8Read
1241
1296
  });
1242
1297
 
1243
1298
  const DEFINITION_MISMATCH = -1;
1244
1299
  function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
1245
- const $root = decoder.$root;
1300
+ const $root = decoder.root;
1246
1301
  const previousValue = ref[$getByIndex](index);
1247
1302
  let value;
1248
1303
  if ((operation & OPERATION.DELETE) === OPERATION.DELETE) {
@@ -1428,7 +1483,8 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
1428
1483
  };
1429
1484
  const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1430
1485
  // "uncompressed" index + operation (array/map items)
1431
- const operation = bytes[it.offset++];
1486
+ let operation = bytes[it.offset++];
1487
+ let index;
1432
1488
  if (operation === OPERATION.CLEAR) {
1433
1489
  //
1434
1490
  // When decoding:
@@ -1442,8 +1498,8 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1442
1498
  else if (operation === OPERATION.DELETE_BY_REFID) {
1443
1499
  // TODO: refactor here, try to follow same flow as below
1444
1500
  const refId = number(bytes, it);
1445
- const previousValue = decoder.$root.refs.get(refId);
1446
- const index = ref.findIndex((value) => value === previousValue);
1501
+ const previousValue = decoder.root.refs.get(refId);
1502
+ index = ref.findIndex((value) => value === previousValue);
1447
1503
  ref[$deleteByIndex](index);
1448
1504
  allChanges.push({
1449
1505
  ref,
@@ -1456,7 +1512,18 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1456
1512
  });
1457
1513
  return;
1458
1514
  }
1459
- const index = number(bytes, it);
1515
+ else if (operation === OPERATION.ADD_BY_REFID) {
1516
+ // operation = OPERATION.ADD;
1517
+ const refId = number(bytes, it);
1518
+ const itemByRefId = decoder.root.refs.get(refId);
1519
+ // use existing index, or push new value
1520
+ index = (itemByRefId)
1521
+ ? ref.findIndex((value) => value === itemByRefId)
1522
+ : ref.length;
1523
+ }
1524
+ else {
1525
+ index = number(bytes, it);
1526
+ }
1460
1527
  const type = ref[$childType];
1461
1528
  let dynamicIndex = index;
1462
1529
  const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
@@ -1865,14 +1932,6 @@ class ArraySchema {
1865
1932
  lastIndexOf(searchElement, fromIndex = this.length - 1) {
1866
1933
  return this.items.lastIndexOf(searchElement, fromIndex);
1867
1934
  }
1868
- /**
1869
- * Determines whether all the members of an array satisfy the specified test.
1870
- * @param callbackfn A function that accepts up to three arguments. The every method calls
1871
- * the callbackfn function for each element in the array until the callbackfn returns a value
1872
- * which is coercible to the Boolean value false, or until the end of the array.
1873
- * @param thisArg An object to which the this keyword can refer in the callbackfn function.
1874
- * If thisArg is omitted, undefined is used as the this value.
1875
- */
1876
1935
  every(callbackfn, thisArg) {
1877
1936
  return this.items.every(callbackfn, thisArg);
1878
1937
  }
@@ -2551,6 +2610,7 @@ function view(tag = DEFAULT_VIEW_TAG) {
2551
2610
  const constructor = target.constructor;
2552
2611
  const parentClass = Object.getPrototypeOf(constructor);
2553
2612
  const parentMetadata = parentClass[Symbol.metadata];
2613
+ // TODO: use Metadata.initialize()
2554
2614
  const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
2555
2615
  if (!metadata[fieldName]) {
2556
2616
  //
@@ -2575,8 +2635,8 @@ function type(type, options) {
2575
2635
  // for inheritance support
2576
2636
  TypeContext.register(constructor);
2577
2637
  const parentClass = Object.getPrototypeOf(constructor);
2578
- const parentMetadata = parentClass[Symbol.metadata];
2579
- const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
2638
+ const parentMetadata = parentClass && parentClass[Symbol.metadata];
2639
+ const metadata = Metadata.initialize(constructor, parentMetadata);
2580
2640
  let fieldIndex;
2581
2641
  /**
2582
2642
  * skip if descriptor already exists for this field (`@deprecated()`)
@@ -3349,6 +3409,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
3349
3409
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
3350
3410
  PERFORMANCE OF THIS SOFTWARE.
3351
3411
  ***************************************************************************** */
3412
+ /* global Reflect, Promise, SuppressedError, Symbol */
3413
+
3352
3414
 
3353
3415
  function __decorate(decorators, target, key, desc) {
3354
3416
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -3380,11 +3442,14 @@ class Encoder {
3380
3442
  setRoot(state) {
3381
3443
  this.root = new Root();
3382
3444
  this.state = state;
3445
+ // Workaround to allow using an empty Schema.
3446
+ if (state.constructor[Symbol.metadata] === undefined) {
3447
+ Metadata.init(state);
3448
+ }
3383
3449
  state[$changes].setRoot(this.root);
3384
3450
  }
3385
- encode(it = { offset: 0 }, view, bytes = this.sharedBuffer, changeTrees = this.root.changes) {
3451
+ encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees) {
3386
3452
  const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
3387
- const isEncodeAll = this.root.allChanges === changeTrees;
3388
3453
  const hasView = (view !== undefined);
3389
3454
  const rootChangeTree = this.state[$changes];
3390
3455
  const changeTreesIterator = changeTrees.entries();
@@ -3393,6 +3458,12 @@ class Encoder {
3393
3458
  const ctor = ref['constructor'];
3394
3459
  const encoder = ctor[$encoder];
3395
3460
  const filter = ctor[$filter];
3461
+ // try { throw new Error(); } catch (e) {
3462
+ // // only print if not coming from Reflection.ts
3463
+ // if (!e.stack.includes("src/Reflection.ts")) {
3464
+ // console.log("ChangeTree:", { ref: ref.constructor.name, });
3465
+ // }
3466
+ // }
3396
3467
  if (hasView) {
3397
3468
  if (!view.items.has(changeTree)) {
3398
3469
  view.invisible.add(changeTree);
@@ -3404,8 +3475,8 @@ class Encoder {
3404
3475
  }
3405
3476
  // skip root `refId` if it's the first change tree
3406
3477
  if (it.offset !== initialOffset || changeTree !== rootChangeTree) {
3407
- bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3408
- number$1(bytes, changeTree.refId, it);
3478
+ buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3479
+ number$1(buffer, changeTree.refId, it);
3409
3480
  }
3410
3481
  const changesIterator = changes.entries();
3411
3482
  for (const [fieldIndex, operation] of changesIterator) {
@@ -3422,22 +3493,35 @@ class Encoder {
3422
3493
  // view?.invisible.add(changeTree);
3423
3494
  continue;
3424
3495
  }
3425
- // console.log("WILL ENCODE", {
3426
- // ref: changeTree.ref.constructor.name,
3427
- // fieldIndex,
3428
- // operation: OPERATION[operation],
3429
- // });
3430
- encoder(this, bytes, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3496
+ // try { throw new Error(); } catch (e) {
3497
+ // // only print if not coming from Reflection.ts
3498
+ // if (!e.stack.includes("src/Reflection.ts")) {
3499
+ // console.log("WILL ENCODE", {
3500
+ // ref: changeTree.ref.constructor.name,
3501
+ // fieldIndex,
3502
+ // operation: OPERATION[operation],
3503
+ // });
3504
+ // }
3505
+ // }
3506
+ encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3431
3507
  }
3432
3508
  }
3433
- if (it.offset > bytes.byteLength) {
3434
- const newSize = getNextPowerOf2(this.sharedBuffer.byteLength * 2);
3435
- console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + bytes.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
3509
+ if (it.offset > buffer.byteLength) {
3510
+ const newSize = getNextPowerOf2(buffer.byteLength * 2);
3511
+ console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
3512
+
3513
+ import { Encoder } from "@colyseus/schema";
3514
+ Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
3515
+ `);
3436
3516
  //
3437
3517
  // resize buffer and re-encode (TODO: can we avoid re-encoding here?)
3438
3518
  //
3439
- this.sharedBuffer = Buffer.allocUnsafeSlow(newSize);
3440
- return this.encode({ offset: initialOffset }, view);
3519
+ buffer = Buffer.allocUnsafeSlow(newSize);
3520
+ // assign resized buffer to local sharedBuffer
3521
+ if (buffer === this.sharedBuffer) {
3522
+ this.sharedBuffer = buffer;
3523
+ }
3524
+ return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
3441
3525
  }
3442
3526
  else {
3443
3527
  //
@@ -3449,38 +3533,37 @@ class Encoder {
3449
3533
  //
3450
3534
  this.onEndEncode(changeTrees);
3451
3535
  }
3452
- // return bytes;
3453
- return bytes.slice(0, it.offset);
3536
+ return buffer.subarray(0, it.offset);
3454
3537
  }
3455
3538
  }
3456
- encodeAll(it = { offset: 0 }) {
3457
- // console.log(`encodeAll(), this.$root.allChanges (${this.$root.allChanges.size})`);
3458
- // Array.from(this.$root.allChanges.entries()).map((item) => {
3459
- // console.log("->", item[0].refId, item[0].ref.toJSON());
3539
+ encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
3540
+ // console.log(`encodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
3541
+ // Array.from(this.root.allChanges.entries()).map((item) => {
3542
+ // console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
3460
3543
  // });
3461
- return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
3544
+ return this.encode(it, undefined, buffer, this.root.allChanges, true);
3462
3545
  }
3463
3546
  encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3464
3547
  const viewOffset = it.offset;
3465
- // console.log(`encodeAllView(), this.$root.allFilteredChanges (${this.$root.allFilteredChanges.size})`);
3548
+ // console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
3466
3549
  // this.debugAllFilteredChanges();
3467
3550
  // try to encode "filtered" changes
3468
- this.encode(it, view, bytes, this.root.allFilteredChanges);
3551
+ this.encode(it, view, bytes, this.root.allFilteredChanges, true);
3469
3552
  return Buffer.concat([
3470
- bytes.slice(0, sharedOffset),
3471
- bytes.slice(viewOffset, it.offset)
3553
+ bytes.subarray(0, sharedOffset),
3554
+ bytes.subarray(viewOffset, it.offset)
3472
3555
  ]);
3473
3556
  }
3474
- // debugAllFilteredChanges() {
3475
- // Array.from(this.$root.allFilteredChanges.entries()).map((item) => {
3476
- // console.log("->", { refId: item[0].refId }, item[0].ref.toJSON());
3477
- // if (Array.isArray(item[0].ref.toJSON())) {
3478
- // item[1].forEach((op, key) => {
3479
- // console.log(" ->", { key, op: OPERATION[op] });
3480
- // })
3481
- // }
3482
- // });
3483
- // }
3557
+ debugAllFilteredChanges() {
3558
+ Array.from(this.root.allFilteredChanges.entries()).map((item) => {
3559
+ console.log("->", { refId: item[0].refId, changes: item[1].size }, item[0].ref.toJSON());
3560
+ if (Array.isArray(item[0].ref.toJSON())) {
3561
+ item[1].forEach((op, key) => {
3562
+ console.log(" ->", { key, op: OPERATION[op] });
3563
+ });
3564
+ }
3565
+ });
3566
+ }
3484
3567
  encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3485
3568
  const viewOffset = it.offset;
3486
3569
  // try to encode "filtered" changes
@@ -3512,8 +3595,8 @@ class Encoder {
3512
3595
  // clear "view" changes after encoding
3513
3596
  view.changes.clear();
3514
3597
  return Buffer.concat([
3515
- bytes.slice(0, sharedOffset),
3516
- bytes.slice(viewOffset, it.offset)
3598
+ bytes.subarray(0, sharedOffset),
3599
+ bytes.subarray(viewOffset, it.offset)
3517
3600
  ]);
3518
3601
  }
3519
3602
  onEndEncode(changeTrees = this.root.changes) {
@@ -3692,12 +3775,12 @@ class Decoder {
3692
3775
  }
3693
3776
  setRoot(root) {
3694
3777
  this.state = root;
3695
- this.$root = new ReferenceTracker();
3696
- this.$root.addRef(0, root);
3778
+ this.root = new ReferenceTracker();
3779
+ this.root.addRef(0, root);
3697
3780
  }
3698
3781
  decode(bytes, it = { offset: 0 }, ref = this.state) {
3699
3782
  const allChanges = [];
3700
- const $root = this.$root;
3783
+ const $root = this.root;
3701
3784
  const totalBytes = bytes.byteLength;
3702
3785
  let decoder = ref['constructor'][$decoder];
3703
3786
  this.currentRefId = 0;
@@ -3778,7 +3861,7 @@ class Decoder {
3778
3861
  previousValue: value
3779
3862
  });
3780
3863
  if (needRemoveRef) {
3781
- this.$root.removeRef(this.$root.refIds.get(value));
3864
+ this.root.removeRef(this.root.refIds.get(value));
3782
3865
  }
3783
3866
  });
3784
3867
  }
@@ -3818,7 +3901,7 @@ class Reflection extends Schema {
3818
3901
  super(...arguments);
3819
3902
  this.types = new ArraySchema();
3820
3903
  }
3821
- static encode(instance, context) {
3904
+ static encode(instance, context, it = { offset: 0 }) {
3822
3905
  if (!context) {
3823
3906
  context = new TypeContext(instance.constructor);
3824
3907
  }
@@ -3875,7 +3958,6 @@ class Reflection extends Schema {
3875
3958
  }
3876
3959
  buildType(type, klass[Symbol.metadata]);
3877
3960
  }
3878
- const it = { offset: 0 };
3879
3961
  const buf = encoder.encodeAll(it);
3880
3962
  return Buffer.from(buf, 0, it.offset);
3881
3963
  }
@@ -3883,59 +3965,298 @@ class Reflection extends Schema {
3883
3965
  const reflection = new Reflection();
3884
3966
  const reflectionDecoder = new Decoder(reflection);
3885
3967
  reflectionDecoder.decode(bytes, it);
3886
- const context = new TypeContext();
3887
- const schemaTypes = reflection.types.reduce((types, reflectionType) => {
3888
- const parentKlass = types[reflectionType.extendsId] || Schema;
3889
- const schema = class _ extends parentKlass {
3968
+ const typeContext = new TypeContext();
3969
+ // 1st pass, initialize metadata + inheritance
3970
+ reflection.types.forEach((reflectionType) => {
3971
+ const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
3972
+ const schema = class _ extends parentClass {
3890
3973
  };
3891
- // const _metadata = Object.create(_classSuper[Symbol.metadata] ?? null);
3892
- const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
3893
- Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
3974
+ const parentMetadata = parentClass[Symbol.metadata];
3894
3975
  // register for inheritance support
3895
3976
  TypeContext.register(schema);
3896
- const typeid = reflectionType.id;
3897
- types[typeid] = schema;
3898
- context.add(schema, typeid);
3899
- return types;
3977
+ // for inheritance support
3978
+ Metadata.initialize(schema, parentMetadata);
3979
+ typeContext.add(schema, reflectionType.id);
3900
3980
  }, {});
3981
+ // 2nd pass, set fields
3901
3982
  reflection.types.forEach((reflectionType) => {
3902
- const schemaType = schemaTypes[reflectionType.id];
3983
+ const schemaType = typeContext.get(reflectionType.id);
3903
3984
  const metadata = schemaType[Symbol.metadata];
3904
- const parentKlass = reflection.types[reflectionType.extendsId];
3905
- const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
3985
+ // FIXME: use metadata[-1] to get field count
3986
+ const parentFieldIndex = 0;
3987
+ // console.log("--------------------");
3988
+ // // console.log("reflectionType", reflectionType.toJSON());
3989
+ // console.log("reflectionType.fields", reflectionType.fields.toJSON());
3990
+ // console.log("parentFieldIndex", parentFieldIndex);
3991
+ //
3992
+ // FIXME: set fields using parentKlass as well
3993
+ // currently the fields are duplicated on inherited classes
3994
+ //
3995
+ // // const parentKlass = reflection.types[reflectionType.extendsId];
3996
+ // // parentKlass.fields
3906
3997
  reflectionType.fields.forEach((field, i) => {
3907
3998
  const fieldIndex = parentFieldIndex + i;
3908
3999
  if (field.referencedType !== undefined) {
3909
4000
  let fieldType = field.type;
3910
- let refType = schemaTypes[field.referencedType];
4001
+ let refType = typeContext.get(field.referencedType);
3911
4002
  // map or array of primitive type (-1)
3912
4003
  if (!refType) {
3913
4004
  const typeInfo = field.type.split(":");
3914
4005
  fieldType = typeInfo[0];
3915
- refType = typeInfo[1];
4006
+ refType = typeInfo[1]; // string
3916
4007
  }
3917
4008
  if (fieldType === "ref") {
3918
- // type(refType)(schemaType.prototype, field.name);
3919
4009
  Metadata.addField(metadata, fieldIndex, field.name, refType);
3920
4010
  }
3921
4011
  else {
3922
- // type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
3923
4012
  Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
3924
4013
  }
3925
4014
  }
3926
4015
  else {
3927
- // type(field.type as PrimitiveType)(schemaType.prototype, field.name);
3928
4016
  Metadata.addField(metadata, fieldIndex, field.name, field.type);
3929
4017
  }
3930
4018
  });
3931
4019
  });
3932
- return new (schemaTypes[0])();
4020
+ // @ts-ignore
4021
+ return new (typeContext.get(0))();
3933
4022
  }
3934
4023
  }
3935
4024
  __decorate([
3936
4025
  type([ReflectionType])
3937
4026
  ], Reflection.prototype, "types", void 0);
3938
4027
 
4028
+ function getDecoderStateCallbacks(decoder) {
4029
+ const $root = decoder.root;
4030
+ const callbacks = $root.callbacks;
4031
+ let isTriggeringOnAdd = false;
4032
+ decoder.triggerChanges = function (allChanges) {
4033
+ const uniqueRefIds = new Set();
4034
+ for (let i = 0, l = allChanges.length; i < l; i++) {
4035
+ const change = allChanges[i];
4036
+ const refId = change.refId;
4037
+ const ref = change.ref;
4038
+ const $callbacks = callbacks[refId];
4039
+ if (!$callbacks) {
4040
+ continue;
4041
+ }
4042
+ //
4043
+ // trigger onRemove on child structure.
4044
+ //
4045
+ if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
4046
+ change.previousValue instanceof Schema) {
4047
+ const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE];
4048
+ for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
4049
+ deleteCallbacks[i]();
4050
+ }
4051
+ }
4052
+ if (ref instanceof Schema) {
4053
+ //
4054
+ // Handle schema instance
4055
+ //
4056
+ if (!uniqueRefIds.has(refId)) {
4057
+ // trigger onChange
4058
+ const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
4059
+ for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
4060
+ replaceCallbacks[i]();
4061
+ // try {
4062
+ // } catch (e) {
4063
+ // console.error(e);
4064
+ // }
4065
+ }
4066
+ }
4067
+ if ($callbacks.hasOwnProperty(change.field)) {
4068
+ const fieldCallbacks = $callbacks[change.field];
4069
+ for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
4070
+ fieldCallbacks[i](change.value, change.previousValue);
4071
+ // try {
4072
+ // } catch (e) {
4073
+ // console.error(e);
4074
+ // }
4075
+ }
4076
+ }
4077
+ }
4078
+ else {
4079
+ //
4080
+ // Handle collection of items
4081
+ //
4082
+ if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
4083
+ //
4084
+ // FIXME: `previousValue` should always be available.
4085
+ //
4086
+ if (change.previousValue !== undefined) {
4087
+ // triger onRemove
4088
+ const deleteCallbacks = $callbacks[OPERATION.DELETE];
4089
+ for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
4090
+ deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
4091
+ }
4092
+ }
4093
+ // Handle DELETE_AND_ADD operations
4094
+ // FIXME: should we set "isTriggeringOnAdd" here?
4095
+ if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
4096
+ const addCallbacks = $callbacks[OPERATION.ADD];
4097
+ for (let i = addCallbacks?.length - 1; i >= 0; i--) {
4098
+ addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4099
+ }
4100
+ }
4101
+ }
4102
+ else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
4103
+ // triger onAdd
4104
+ isTriggeringOnAdd = true;
4105
+ const addCallbacks = $callbacks[OPERATION.ADD];
4106
+ for (let i = addCallbacks?.length - 1; i >= 0; i--) {
4107
+ addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4108
+ }
4109
+ isTriggeringOnAdd = false;
4110
+ }
4111
+ // trigger onChange
4112
+ if (change.value !== change.previousValue) {
4113
+ const replaceCallbacks = $callbacks[OPERATION.REPLACE];
4114
+ for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
4115
+ replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4116
+ }
4117
+ }
4118
+ }
4119
+ uniqueRefIds.add(refId);
4120
+ }
4121
+ };
4122
+ function getProxy(metadataOrType, context) {
4123
+ let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
4124
+ let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
4125
+ (metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
4126
+ if (metadata && !isCollection) {
4127
+ const onAdd = function (ref, prop, callback, immediate) {
4128
+ // immediate trigger
4129
+ if (immediate &&
4130
+ context.instance[prop] !== undefined &&
4131
+ !isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
4132
+ ) {
4133
+ callback(context.instance[prop], undefined);
4134
+ }
4135
+ return $root.addCallback($root.refIds.get(ref), prop, callback);
4136
+ };
4137
+ /**
4138
+ * Schema instances
4139
+ */
4140
+ return new Proxy({
4141
+ listen: function listen(prop, callback, immediate = true) {
4142
+ if (context.instance) {
4143
+ return onAdd(context.instance, prop, callback, immediate);
4144
+ }
4145
+ else {
4146
+ // collection instance not received yet
4147
+ context.onInstanceAvailable((ref, existing) => onAdd(ref, prop, callback, immediate && existing));
4148
+ }
4149
+ },
4150
+ onChange: function onChange(callback) {
4151
+ return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, callback);
4152
+ },
4153
+ bindTo: function bindTo(targetObject, properties) {
4154
+ //
4155
+ // TODO: refactor this implementation. There is room for improvement here.
4156
+ //
4157
+ if (!properties) {
4158
+ properties = Object.keys(metadata);
4159
+ }
4160
+ return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, () => {
4161
+ properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
4162
+ });
4163
+ }
4164
+ }, {
4165
+ get(target, prop) {
4166
+ if (metadata[prop]) {
4167
+ const instance = context.instance?.[prop];
4168
+ const onInstanceAvailable = ((callback) => {
4169
+ const unbind = $(context.instance).listen(prop, (value, _) => {
4170
+ callback(value, false);
4171
+ // FIXME: by "unbinding" the callback here,
4172
+ // it will not support when the server
4173
+ // re-instantiates the instance.
4174
+ //
4175
+ unbind?.();
4176
+ }, false);
4177
+ // has existing value
4178
+ if ($root.refIds.get(instance) !== undefined) {
4179
+ callback(instance, true);
4180
+ }
4181
+ });
4182
+ return getProxy(metadata[prop].type, {
4183
+ instance,
4184
+ parentInstance: context.instance,
4185
+ onInstanceAvailable,
4186
+ });
4187
+ }
4188
+ else {
4189
+ // accessing the function
4190
+ return target[prop];
4191
+ }
4192
+ },
4193
+ has(target, prop) { return metadata[prop] !== undefined; },
4194
+ set(_, _1, _2) { throw new Error("not allowed"); },
4195
+ deleteProperty(_, _1) { throw new Error("not allowed"); },
4196
+ });
4197
+ }
4198
+ else {
4199
+ /**
4200
+ * Collection instances
4201
+ */
4202
+ const onAdd = function (ref, callback, immediate) {
4203
+ // Trigger callback on existing items
4204
+ if (immediate) {
4205
+ ref.forEach((v, k) => callback(v, k));
4206
+ }
4207
+ return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, callback);
4208
+ };
4209
+ const onRemove = function (ref, callback) {
4210
+ return $root.addCallback($root.refIds.get(ref), OPERATION.DELETE, callback);
4211
+ };
4212
+ return new Proxy({
4213
+ onAdd: function (callback, immediate = true) {
4214
+ //
4215
+ // https://github.com/colyseus/schema/issues/147
4216
+ // If parent instance has "onAdd" registered, avoid triggering immediate callback.
4217
+ //
4218
+ // FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
4219
+ //
4220
+ if (context.onInstanceAvailable) {
4221
+ // collection instance not received yet
4222
+ context.onInstanceAvailable((ref, existing) => onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd));
4223
+ }
4224
+ else if (context.instance) {
4225
+ onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
4226
+ }
4227
+ },
4228
+ onRemove: function (callback) {
4229
+ if (context.onInstanceAvailable) {
4230
+ // collection instance not received yet
4231
+ context.onInstanceAvailable((ref) => onRemove(ref, callback));
4232
+ }
4233
+ else if (context.instance) {
4234
+ onRemove(context.instance, callback);
4235
+ }
4236
+ },
4237
+ }, {
4238
+ get(target, prop) {
4239
+ if (!target[prop]) {
4240
+ throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
4241
+ }
4242
+ return target[prop];
4243
+ },
4244
+ has(target, prop) { return target[prop] !== undefined; },
4245
+ set(_, _1, _2) { throw new Error("not allowed"); },
4246
+ deleteProperty(_, _1) { throw new Error("not allowed"); },
4247
+ });
4248
+ }
4249
+ }
4250
+ function $(instance) {
4251
+ return getProxy(undefined, { instance });
4252
+ }
4253
+ return $;
4254
+ }
4255
+
4256
+ function getRawChangesCallback(decoder, callback) {
4257
+ decoder.triggerChanges = callback;
4258
+ }
4259
+
3939
4260
  class StateView {
3940
4261
  constructor() {
3941
4262
  /**
@@ -3958,12 +4279,18 @@ class StateView {
3958
4279
  console.warn("StateView#add(), invalid object:", obj);
3959
4280
  return this;
3960
4281
  }
4282
+ // FIXME: ArraySchema/MapSchema does not have metadata
4283
+ const metadata = obj.constructor[Symbol.metadata];
3961
4284
  let changeTree = obj[$changes];
3962
4285
  this.items.add(changeTree);
3963
4286
  // Add children of this ChangeTree to this view
3964
- changeTree.forEachChild((change, _) => this.add(change.ref, tag));
3965
- // FIXME: ArraySchema/MapSchema does not have metadata
3966
- const metadata = obj.constructor[Symbol.metadata];
4287
+ changeTree.forEachChild((change, index) => {
4288
+ // Do not ADD children that don't have the same tag
4289
+ if (metadata && metadata[metadata[index]].tag !== tag) {
4290
+ return;
4291
+ }
4292
+ this.add(change.ref, tag);
4293
+ });
3967
4294
  // add parent ChangeTree's, if they are invisible to this view
3968
4295
  // TODO: REFACTOR addParent()
3969
4296
  this.addParent(changeTree, tag);
@@ -3990,7 +4317,6 @@ class StateView {
3990
4317
  tags = this.tags.get(changeTree);
3991
4318
  }
3992
4319
  tags.add(tag);
3993
- // console.log("BY TAG:", tag);
3994
4320
  // Ref: add tagged properties
3995
4321
  metadata?.[-3]?.[tag]?.forEach((index) => {
3996
4322
  if (changeTree.getChange(index) !== OPERATION.DELETE) {
@@ -4129,5 +4455,5 @@ registerType("array", { constructor: ArraySchema });
4129
4455
  registerType("set", { constructor: SetSchema });
4130
4456
  registerType("collection", { constructor: CollectionSchema, });
4131
4457
 
4132
- export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, registerType, type, view };
4458
+ export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, getDecoderStateCallbacks, getRawChangesCallback, registerType, type, view };
4133
4459
  //# sourceMappingURL=index.mjs.map