@colyseus/schema 3.0.0-alpha.3 → 3.0.0-alpha.30

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 (116) hide show
  1. package/README.md +131 -61
  2. package/build/cjs/index.js +644 -283
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +643 -282
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +644 -283
  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 +2 -3
  11. package/lib/Reflection.js +31 -26
  12. package/lib/Reflection.js.map +1 -1
  13. package/lib/Schema.d.ts +2 -2
  14. package/lib/Schema.js +2 -2
  15. package/lib/Schema.js.map +1 -1
  16. package/lib/annotations.d.ts +0 -19
  17. package/lib/annotations.js +15 -101
  18. package/lib/annotations.js.map +1 -1
  19. package/lib/bench_encode.d.ts +1 -0
  20. package/lib/bench_encode.js +120 -0
  21. package/lib/bench_encode.js.map +1 -0
  22. package/lib/codegen/api.js +1 -2
  23. package/lib/codegen/api.js.map +1 -1
  24. package/lib/codegen/languages/cpp.js +1 -2
  25. package/lib/codegen/languages/cpp.js.map +1 -1
  26. package/lib/codegen/languages/csharp.js +1 -2
  27. package/lib/codegen/languages/csharp.js.map +1 -1
  28. package/lib/codegen/languages/haxe.js +1 -2
  29. package/lib/codegen/languages/haxe.js.map +1 -1
  30. package/lib/codegen/languages/java.js +1 -2
  31. package/lib/codegen/languages/java.js.map +1 -1
  32. package/lib/codegen/languages/js.js +1 -2
  33. package/lib/codegen/languages/js.js.map +1 -1
  34. package/lib/codegen/languages/lua.js +1 -2
  35. package/lib/codegen/languages/lua.js.map +1 -1
  36. package/lib/codegen/languages/ts.js +1 -2
  37. package/lib/codegen/languages/ts.js.map +1 -1
  38. package/lib/codegen/parser.js +2 -3
  39. package/lib/codegen/parser.js.map +1 -1
  40. package/lib/codegen/types.js +3 -3
  41. package/lib/codegen/types.js.map +1 -1
  42. package/lib/debug.d.ts +1 -0
  43. package/lib/debug.js +52 -0
  44. package/lib/debug.js.map +1 -0
  45. package/lib/decoder/DecodeOperation.d.ts +0 -1
  46. package/lib/decoder/DecodeOperation.js +23 -7
  47. package/lib/decoder/DecodeOperation.js.map +1 -1
  48. package/lib/decoder/Decoder.d.ts +6 -7
  49. package/lib/decoder/Decoder.js +8 -8
  50. package/lib/decoder/Decoder.js.map +1 -1
  51. package/lib/decoder/strategy/RawChanges.js +1 -2
  52. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  53. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  54. package/lib/decoder/strategy/StateCallbacks.js +72 -63
  55. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  56. package/lib/encoder/ChangeTree.d.ts +1 -12
  57. package/lib/encoder/ChangeTree.js +29 -58
  58. package/lib/encoder/ChangeTree.js.map +1 -1
  59. package/lib/encoder/EncodeOperation.d.ts +0 -1
  60. package/lib/encoder/EncodeOperation.js +29 -13
  61. package/lib/encoder/EncodeOperation.js.map +1 -1
  62. package/lib/encoder/Encoder.d.ts +10 -8
  63. package/lib/encoder/Encoder.js +78 -53
  64. package/lib/encoder/Encoder.js.map +1 -1
  65. package/lib/encoder/Root.d.ts +17 -0
  66. package/lib/encoder/Root.js +44 -0
  67. package/lib/encoder/Root.js.map +1 -0
  68. package/lib/encoder/StateView.d.ts +2 -2
  69. package/lib/encoder/StateView.js +48 -58
  70. package/lib/encoder/StateView.js.map +1 -1
  71. package/lib/encoding/assert.js +3 -3
  72. package/lib/encoding/assert.js.map +1 -1
  73. package/lib/encoding/decode.js +21 -22
  74. package/lib/encoding/decode.js.map +1 -1
  75. package/lib/encoding/encode.d.ts +2 -2
  76. package/lib/encoding/encode.js +40 -39
  77. package/lib/encoding/encode.js.map +1 -1
  78. package/lib/encoding/spec.d.ts +2 -1
  79. package/lib/encoding/spec.js +1 -0
  80. package/lib/encoding/spec.js.map +1 -1
  81. package/lib/index.d.ts +5 -1
  82. package/lib/index.js +8 -4
  83. package/lib/index.js.map +1 -1
  84. package/lib/types/TypeContext.d.ts +23 -0
  85. package/lib/types/TypeContext.js +109 -0
  86. package/lib/types/TypeContext.js.map +1 -0
  87. package/lib/types/custom/ArraySchema.d.ts +2 -2
  88. package/lib/types/custom/ArraySchema.js +0 -9
  89. package/lib/types/custom/ArraySchema.js.map +1 -1
  90. package/lib/types/registry.js +3 -4
  91. package/lib/types/registry.js.map +1 -1
  92. package/lib/types/utils.js +1 -2
  93. package/lib/types/utils.js.map +1 -1
  94. package/lib/utils.js +3 -4
  95. package/lib/utils.js.map +1 -1
  96. package/package.json +5 -5
  97. package/src/Metadata.ts +47 -0
  98. package/src/Reflection.ts +34 -26
  99. package/src/Schema.ts +2 -2
  100. package/src/annotations.ts +7 -109
  101. package/src/bench_encode.ts +97 -0
  102. package/src/debug.ts +56 -0
  103. package/src/decoder/DecodeOperation.ts +30 -7
  104. package/src/decoder/Decoder.ts +13 -11
  105. package/src/decoder/strategy/StateCallbacks.ts +149 -79
  106. package/src/encoder/ChangeTree.ts +36 -66
  107. package/src/encoder/EncodeOperation.ts +29 -12
  108. package/src/encoder/Encoder.ts +95 -61
  109. package/src/encoder/Root.ts +51 -0
  110. package/src/encoder/StateView.ts +51 -67
  111. package/src/encoding/decode.ts +1 -2
  112. package/src/encoding/encode.ts +25 -22
  113. package/src/encoding/spec.ts +1 -0
  114. package/src/index.ts +8 -11
  115. package/src/types/TypeContext.ts +127 -0
  116. package/src/types/custom/ArraySchema.ts +2 -2
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  const SWITCH_TO_STRUCTURE = 255; // (decoding collides with DELETE_AND_ADD + fieldIndex = 63)
6
4
  const TYPE_ID = 213;
7
5
  /**
@@ -27,6 +25,7 @@ exports.OPERATION = void 0;
27
25
  OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
28
26
  OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
29
27
  OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
28
+ OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
30
29
  })(exports.OPERATION || (exports.OPERATION = {}));
31
30
 
32
31
  Symbol.metadata ??= Symbol.for("Symbol.metadata");
@@ -143,6 +142,45 @@ const Metadata = {
143
142
  isDeprecated(metadata, field) {
144
143
  return metadata[field].deprecated === true;
145
144
  },
145
+ init(klass) {
146
+ //
147
+ // Used only to initialize an empty Schema (Encoder#constructor)
148
+ // TODO: remove/refactor this...
149
+ //
150
+ const metadata = {};
151
+ klass[Symbol.metadata] = metadata;
152
+ Object.defineProperty(metadata, -1, {
153
+ value: 0,
154
+ enumerable: false,
155
+ configurable: true,
156
+ });
157
+ },
158
+ initialize(constructor, parentMetadata) {
159
+ let metadata = constructor[Symbol.metadata] ?? Object.create(null);
160
+ // make sure inherited classes have their own metadata object.
161
+ if (constructor[Symbol.metadata] === parentMetadata) {
162
+ metadata = Object.create(null);
163
+ if (parentMetadata) {
164
+ // assign parent metadata to current
165
+ Object.assign(metadata, parentMetadata);
166
+ for (let i = 0; i <= parentMetadata[-1]; i++) {
167
+ Object.defineProperty(metadata, i, {
168
+ value: parentMetadata[i],
169
+ enumerable: false,
170
+ configurable: true,
171
+ });
172
+ }
173
+ Object.defineProperty(metadata, -1, {
174
+ value: parentMetadata[-1],
175
+ enumerable: false,
176
+ configurable: true,
177
+ writable: true,
178
+ });
179
+ }
180
+ }
181
+ constructor[Symbol.metadata] = metadata;
182
+ return metadata;
183
+ },
146
184
  isValidInstance(klass) {
147
185
  return (klass.constructor[Symbol.metadata] &&
148
186
  Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], -1));
@@ -158,44 +196,6 @@ const Metadata = {
158
196
  };
159
197
 
160
198
  var _a$5;
161
- class Root {
162
- constructor() {
163
- this.nextUniqueId = 0;
164
- this.refCount = new WeakMap();
165
- // all changes
166
- this.allChanges = new Map();
167
- this.allFilteredChanges = new Map();
168
- // pending changes to be encoded
169
- this.changes = new Map();
170
- this.filteredChanges = new Map();
171
- }
172
- getNextUniqueId() {
173
- return this.nextUniqueId++;
174
- }
175
- add(changeTree) {
176
- const refCount = this.refCount.get(changeTree) || 0;
177
- this.refCount.set(changeTree, refCount + 1);
178
- }
179
- remove(changeTree) {
180
- const refCount = this.refCount.get(changeTree);
181
- if (refCount <= 1) {
182
- this.allChanges.delete(changeTree);
183
- this.changes.delete(changeTree);
184
- if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
185
- this.allFilteredChanges.delete(changeTree);
186
- this.filteredChanges.delete(changeTree);
187
- }
188
- this.refCount.delete(changeTree);
189
- }
190
- else {
191
- this.refCount.set(changeTree, refCount - 1);
192
- }
193
- changeTree.forEachChild((child, _) => this.remove(child));
194
- }
195
- clear() {
196
- this.changes.clear();
197
- }
198
- }
199
199
  class ChangeTree {
200
200
  static { _a$5 = $isNew; }
201
201
  ;
@@ -227,8 +227,6 @@ class ChangeTree {
227
227
  if (this.isFiltered || this.isPartiallyFiltered) {
228
228
  this.root.allFilteredChanges.set(this, this.allFilteredChanges);
229
229
  this.root.filteredChanges.set(this, this.filteredChanges);
230
- // } else {
231
- // this.root.allChanges.set(this, this.allChanges);
232
230
  }
233
231
  if (!this.isFiltered) {
234
232
  this.root.allChanges.set(this, this.allChanges);
@@ -262,14 +260,12 @@ class ChangeTree {
262
260
  this.checkIsFiltered(parent, parentIndex);
263
261
  if (!this.isFiltered) {
264
262
  this.root.changes.set(this, this.changes);
263
+ this.root.allChanges.set(this, this.allChanges);
265
264
  }
266
265
  if (this.isFiltered || this.isPartiallyFiltered) {
267
266
  this.root.filteredChanges.set(this, this.filteredChanges);
268
267
  this.root.allFilteredChanges.set(this, this.filteredChanges);
269
268
  }
270
- else {
271
- this.root.allChanges.set(this, this.allChanges);
272
- }
273
269
  this.ensureRefId();
274
270
  this.forEachChild((changeTree, atIndex) => {
275
271
  changeTree.setParent(this.ref, root, atIndex);
@@ -293,7 +289,7 @@ class ChangeTree {
293
289
  // MapSchema / ArraySchema, etc.
294
290
  this.ref.forEach((value, key) => {
295
291
  if (Metadata.isValidInstance(value)) {
296
- callback(value[$changes], this.ref[$changes].indexes[key]);
292
+ callback(value[$changes], this.ref[$changes].indexes[key] ?? key);
297
293
  }
298
294
  });
299
295
  }
@@ -321,8 +317,9 @@ class ChangeTree {
321
317
  // TODO: are DELETE operations being encoded as ADD here ??
322
318
  //
323
319
  if (isFiltered) {
324
- this.allFilteredChanges.set(index, exports.OPERATION.ADD);
325
320
  this.root?.filteredChanges.set(this, this.filteredChanges);
321
+ this.allFilteredChanges.set(index, exports.OPERATION.ADD);
322
+ this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
326
323
  }
327
324
  else {
328
325
  this.allChanges.set(index, exports.OPERATION.ADD);
@@ -361,7 +358,6 @@ class ChangeTree {
361
358
  }
362
359
  _shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
363
360
  Array.from(allChangeSet.entries()).forEach(([index, op]) => {
364
- // console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
365
361
  if (index >= startIndex) {
366
362
  allChangeSet.delete(index);
367
363
  allChangeSet.set(index + shiftIndex, op);
@@ -502,16 +498,31 @@ class ChangeTree {
502
498
  }
503
499
  checkIsFiltered(parent, parentIndex) {
504
500
  // Detect if current structure has "filters" declared
505
- this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
506
- // TODO: support "partially filtered", where the instance is visible, but only a field is not.
507
- // Detect if parent has "filters" declared
508
- while (parent && !this.isFiltered) {
509
- const metadata = parent['constructor'][Symbol.metadata];
510
- const fieldName = metadata?.[parentIndex];
511
- const isParentOwned = metadata?.[fieldName]?.tag !== undefined;
512
- this.isFiltered = isParentOwned || parent[$changes].isFiltered; // metadata?.[-2]
513
- parent = parent[$changes].parent;
514
- }
501
+ this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
502
+ if (parent && !Metadata.isValidInstance(parent)) {
503
+ const parentChangeTree = parent[$changes];
504
+ parent = parentChangeTree.parent;
505
+ parentIndex = parentChangeTree.parentIndex;
506
+ }
507
+ const parentMetadata = parent?.['constructor']?.[Symbol.metadata];
508
+ this.isFiltered = (parent &&
509
+ parentMetadata?.[-2]?.includes(parentIndex));
510
+ // this.isFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-4];
511
+ // // Detect if parent has "filters" declared
512
+ // while (parent && !this.isFiltered) {
513
+ // const metadata: Metadata = parent['constructor'][Symbol.metadata];
514
+ // // this.isFiltered = metadata?.[-4];
515
+ // const fieldName = metadata?.[parentIndex];
516
+ // const isParentOwned = metadata?.[fieldName]?.tag !== undefined;
517
+ // this.isFiltered = isParentOwned || parent[$changes].isFiltered; // metadata?.[-2]
518
+ // parent = parent[$changes].parent;
519
+ // };
520
+ // console.log("ChangeTree.checkIsFiltered", {
521
+ // parent: parent?.constructor.name,
522
+ // ref: this.ref.constructor.name,
523
+ // isFiltered: this.isFiltered,
524
+ // isPartiallyFiltered: this.isPartiallyFiltered,
525
+ // });
515
526
  //
516
527
  // TODO: refactor this!
517
528
  //
@@ -563,26 +574,29 @@ try {
563
574
  textEncoder = new TextEncoder();
564
575
  }
565
576
  catch (e) { }
566
- function utf8Length(str) {
567
- var c = 0, length = 0;
568
- for (var i = 0, l = str.length; i < l; i++) {
569
- c = str.charCodeAt(i);
570
- if (c < 0x80) {
571
- length += 1;
572
- }
573
- else if (c < 0x800) {
574
- length += 2;
575
- }
576
- else if (c < 0xd800 || c >= 0xe000) {
577
- length += 3;
578
- }
579
- else {
580
- i++;
581
- length += 4;
577
+ const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
578
+ const utf8Length = (hasBufferByteLength)
579
+ ? Buffer.byteLength // node
580
+ : function (str, _) {
581
+ var c = 0, length = 0;
582
+ for (var i = 0, l = str.length; i < l; i++) {
583
+ c = str.charCodeAt(i);
584
+ if (c < 0x80) {
585
+ length += 1;
586
+ }
587
+ else if (c < 0x800) {
588
+ length += 2;
589
+ }
590
+ else if (c < 0xd800 || c >= 0xe000) {
591
+ length += 3;
592
+ }
593
+ else {
594
+ i++;
595
+ length += 4;
596
+ }
582
597
  }
583
- }
584
- return length;
585
- }
598
+ return length;
599
+ };
586
600
  function utf8Write(view, str, it) {
587
601
  var c = 0;
588
602
  for (var i = 0, l = str.length; i < l; i++) {
@@ -677,8 +691,7 @@ function string$1(bytes, value, it) {
677
691
  if (!value) {
678
692
  value = "";
679
693
  }
680
- // let length = utf8Length(value);
681
- let length = Buffer.byteLength(value, "utf8");
694
+ let length = utf8Length(value, "utf8");
682
695
  let size = 0;
683
696
  // fixstr
684
697
  if (length < 0x20) {
@@ -789,23 +802,23 @@ function number$1(bytes, value, it) {
789
802
 
790
803
  var encode = /*#__PURE__*/Object.freeze({
791
804
  __proto__: null,
792
- utf8Length: utf8Length,
793
- utf8Write: utf8Write,
794
- int8: int8$1,
795
- uint8: uint8$1,
805
+ boolean: boolean$1,
806
+ float32: float32$1,
807
+ float64: float64$1,
796
808
  int16: int16$1,
797
- uint16: uint16$1,
798
809
  int32: int32$1,
799
- uint32: uint32$1,
800
810
  int64: int64$1,
811
+ int8: int8$1,
812
+ number: number$1,
813
+ string: string$1,
814
+ uint16: uint16$1,
815
+ uint32: uint32$1,
801
816
  uint64: uint64$1,
802
- float32: float32$1,
803
- float64: float64$1,
817
+ uint8: uint8$1,
818
+ utf8Length: utf8Length,
819
+ utf8Write: utf8Write,
804
820
  writeFloat32: writeFloat32,
805
- writeFloat64: writeFloat64,
806
- boolean: boolean$1,
807
- string: string$1,
808
- number: number$1
821
+ writeFloat64: writeFloat64
809
822
  });
810
823
 
811
824
  class EncodeSchemaError extends Error {
@@ -947,6 +960,18 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
947
960
  }
948
961
  const type = changeTree.getType(field);
949
962
  const value = changeTree.getValue(field);
963
+ // try { throw new Error(); } catch (e) {
964
+ // // only print if not coming from Reflection.ts
965
+ // if (!e.stack.includes("src/Reflection.ts")) {
966
+ // console.log("encodeKeyValueOperation -> ", {
967
+ // ref: changeTree.ref.constructor.name,
968
+ // field,
969
+ // operation: OPERATION[operation],
970
+ // value: value?.toJSON(),
971
+ // items: ref.toJSON(),
972
+ // });
973
+ // }
974
+ // }
950
975
  // TODO: inline this function call small performance gain
951
976
  encodeValue(encoder, bytes, ref, type, value, field, operation, it);
952
977
  };
@@ -956,15 +981,19 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
956
981
  */
957
982
  const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
958
983
  const ref = changeTree.ref;
959
- if (hasView &&
960
- operation === exports.OPERATION.DELETE &&
961
- typeof (changeTree.getType(field)) !== "string") {
962
- // encode delete by refId (array of schemas)
963
- bytes[it.offset++] = exports.OPERATION.DELETE_BY_REFID;
964
- const value = ref['tmpItems'][field];
965
- const refId = value[$changes].refId;
966
- number$1(bytes, refId, it);
967
- return;
984
+ const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
985
+ let refOrIndex;
986
+ if (useOperationByRefId) {
987
+ refOrIndex = ref['tmpItems'][field][$changes].refId;
988
+ if (operation === exports.OPERATION.DELETE) {
989
+ operation = exports.OPERATION.DELETE_BY_REFID;
990
+ }
991
+ else if (operation === exports.OPERATION.ADD) {
992
+ operation = exports.OPERATION.ADD_BY_REFID;
993
+ }
994
+ }
995
+ else {
996
+ refOrIndex = field;
968
997
  }
969
998
  // encode operation
970
999
  bytes[it.offset++] = operation & 255;
@@ -973,7 +1002,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
973
1002
  return;
974
1003
  }
975
1004
  // encode index
976
- number$1(bytes, field, it);
1005
+ number$1(bytes, refOrIndex, it);
977
1006
  // Do not encode value for DELETE operations
978
1007
  if (operation === exports.OPERATION.DELETE) {
979
1008
  return;
@@ -1014,7 +1043,6 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
1014
1043
  * SOFTWARE
1015
1044
  */
1016
1045
  function utf8Read(bytes, it, length) {
1017
- it.offset += length;
1018
1046
  var string = '', chr = 0;
1019
1047
  for (var i = it.offset, end = it.offset + length; i < end; i++) {
1020
1048
  var byte = bytes[i];
@@ -1051,6 +1079,7 @@ function utf8Read(bytes, it, length) {
1051
1079
  // (do not throw error to avoid server/client from crashing due to hack attemps)
1052
1080
  // throw new Error('Invalid byte ' + byte.toString(16));
1053
1081
  }
1082
+ it.offset += length;
1054
1083
  return string;
1055
1084
  }
1056
1085
  function int8(bytes, it) {
@@ -1222,31 +1251,31 @@ function switchStructureCheck(bytes, it) {
1222
1251
 
1223
1252
  var decode = /*#__PURE__*/Object.freeze({
1224
1253
  __proto__: null,
1225
- utf8Read: utf8Read,
1226
- int8: int8,
1227
- uint8: uint8,
1228
- int16: int16,
1229
- uint16: uint16,
1230
- int32: int32,
1231
- uint32: uint32,
1254
+ arrayCheck: arrayCheck,
1255
+ boolean: boolean,
1232
1256
  float32: float32,
1233
1257
  float64: float64,
1258
+ int16: int16,
1259
+ int32: int32,
1234
1260
  int64: int64,
1235
- uint64: uint64,
1261
+ int8: int8,
1262
+ number: number,
1263
+ numberCheck: numberCheck,
1236
1264
  readFloat32: readFloat32,
1237
1265
  readFloat64: readFloat64,
1238
- boolean: boolean,
1239
1266
  string: string,
1240
1267
  stringCheck: stringCheck,
1241
- number: number,
1242
- numberCheck: numberCheck,
1243
- arrayCheck: arrayCheck,
1244
- switchStructureCheck: switchStructureCheck
1268
+ switchStructureCheck: switchStructureCheck,
1269
+ uint16: uint16,
1270
+ uint32: uint32,
1271
+ uint64: uint64,
1272
+ uint8: uint8,
1273
+ utf8Read: utf8Read
1245
1274
  });
1246
1275
 
1247
1276
  const DEFINITION_MISMATCH = -1;
1248
1277
  function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
1249
- const $root = decoder.$root;
1278
+ const $root = decoder.root;
1250
1279
  const previousValue = ref[$getByIndex](index);
1251
1280
  let value;
1252
1281
  if ((operation & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
@@ -1352,6 +1381,7 @@ const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
1352
1381
  // skip early if field is not defined
1353
1382
  const field = metadata[index];
1354
1383
  if (field === undefined) {
1384
+ console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
1355
1385
  return DEFINITION_MISMATCH;
1356
1386
  }
1357
1387
  const { value, previousValue } = decodeValue(decoder, operation, ref, index, metadata[field].type, bytes, it, allChanges);
@@ -1432,7 +1462,8 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
1432
1462
  };
1433
1463
  const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1434
1464
  // "uncompressed" index + operation (array/map items)
1435
- const operation = bytes[it.offset++];
1465
+ let operation = bytes[it.offset++];
1466
+ let index;
1436
1467
  if (operation === exports.OPERATION.CLEAR) {
1437
1468
  //
1438
1469
  // When decoding:
@@ -1446,8 +1477,8 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1446
1477
  else if (operation === exports.OPERATION.DELETE_BY_REFID) {
1447
1478
  // TODO: refactor here, try to follow same flow as below
1448
1479
  const refId = number(bytes, it);
1449
- const previousValue = decoder.$root.refs.get(refId);
1450
- const index = ref.findIndex((value) => value === previousValue);
1480
+ const previousValue = decoder.root.refs.get(refId);
1481
+ index = ref.findIndex((value) => value === previousValue);
1451
1482
  ref[$deleteByIndex](index);
1452
1483
  allChanges.push({
1453
1484
  ref,
@@ -1460,7 +1491,18 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1460
1491
  });
1461
1492
  return;
1462
1493
  }
1463
- const index = number(bytes, it);
1494
+ else if (operation === exports.OPERATION.ADD_BY_REFID) {
1495
+ // operation = OPERATION.ADD;
1496
+ const refId = number(bytes, it);
1497
+ const itemByRefId = decoder.root.refs.get(refId);
1498
+ // use existing index, or push new value
1499
+ index = (itemByRefId)
1500
+ ? ref.findIndex((value) => value === itemByRefId)
1501
+ : ref.length;
1502
+ }
1503
+ else {
1504
+ index = number(bytes, it);
1505
+ }
1464
1506
  const type = ref[$childType];
1465
1507
  let dynamicIndex = index;
1466
1508
  const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
@@ -1621,7 +1663,6 @@ class ArraySchema {
1621
1663
  }
1622
1664
  const changeTree = this[$changes];
1623
1665
  changeTree.indexedOperation(length, exports.OPERATION.ADD, this.items.length);
1624
- // changeTree.indexes[length] = length;
1625
1666
  this.items.push(value);
1626
1667
  this.tmpItems.push(value);
1627
1668
  //
@@ -1869,14 +1910,6 @@ class ArraySchema {
1869
1910
  lastIndexOf(searchElement, fromIndex = this.length - 1) {
1870
1911
  return this.items.lastIndexOf(searchElement, fromIndex);
1871
1912
  }
1872
- /**
1873
- * Determines whether all the members of an array satisfy the specified test.
1874
- * @param callbackfn A function that accepts up to three arguments. The every method calls
1875
- * the callbackfn function for each element in the array until the callbackfn returns a value
1876
- * which is coercible to the Boolean value false, or until the end of the array.
1877
- * @param thisArg An object to which the this keyword can refer in the callbackfn function.
1878
- * If thisArg is omitted, undefined is used as the this value.
1879
- */
1880
1913
  every(callbackfn, thisArg) {
1881
1914
  return this.items.every(callbackfn, thisArg);
1882
1915
  }
@@ -2320,7 +2353,6 @@ class MapSchema {
2320
2353
  }
2321
2354
  registerType("map", { constructor: MapSchema });
2322
2355
 
2323
- const DEFAULT_VIEW_TAG = -1;
2324
2356
  class TypeContext {
2325
2357
  /**
2326
2358
  * For inheritance support
@@ -2342,6 +2374,7 @@ class TypeContext {
2342
2374
  this.types = {};
2343
2375
  this.schemas = new Map();
2344
2376
  this.hasFilters = false;
2377
+ this.parentFiltered = {};
2345
2378
  if (rootClass) {
2346
2379
  this.discoverTypes(rootClass);
2347
2380
  }
@@ -2358,32 +2391,46 @@ class TypeContext {
2358
2391
  return false;
2359
2392
  }
2360
2393
  this.types[typeid] = schema;
2394
+ //
2395
+ // Workaround to allow using an empty Schema (with no `@type()` fields)
2396
+ //
2397
+ if (schema[Symbol.metadata] === undefined) {
2398
+ Metadata.init(schema);
2399
+ }
2361
2400
  this.schemas.set(schema, typeid);
2362
2401
  return true;
2363
2402
  }
2364
2403
  getTypeId(klass) {
2365
2404
  return this.schemas.get(klass);
2366
2405
  }
2367
- discoverTypes(klass) {
2406
+ discoverTypes(klass, parentIndex, parentFieldViewTag) {
2368
2407
  if (!this.add(klass)) {
2369
2408
  return;
2370
2409
  }
2371
2410
  // add classes inherited from this base class
2372
2411
  TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
2373
- this.discoverTypes(child);
2412
+ this.discoverTypes(child, parentIndex, parentFieldViewTag);
2374
2413
  });
2375
- // skip if no fields are defined for this class.
2376
- if (klass[Symbol.metadata] === undefined) {
2377
- klass[Symbol.metadata] = {};
2378
- }
2379
- // const metadata = Metadata.getFor(klass);
2380
- const metadata = klass[Symbol.metadata];
2414
+ const metadata = (klass[Symbol.metadata] ??= {});
2381
2415
  // if any schema/field has filters, mark "context" as having filters.
2382
2416
  if (metadata[-2]) {
2383
2417
  this.hasFilters = true;
2384
2418
  }
2419
+ if (parentFieldViewTag !== undefined) {
2420
+ this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
2421
+ }
2385
2422
  for (const field in metadata) {
2423
+ // //
2424
+ // // Modify the field's metadata to include the parent field's view tag
2425
+ // //
2426
+ // if (
2427
+ // parentFieldViewTag !== undefined &&
2428
+ // metadata[field].tag === undefined
2429
+ // ) {
2430
+ // metadata[field].tag = parentFieldViewTag;
2431
+ // }
2386
2432
  const fieldType = metadata[field].type;
2433
+ const viewTag = metadata[field].tag;
2387
2434
  if (typeof (fieldType) === "string") {
2388
2435
  continue;
2389
2436
  }
@@ -2392,10 +2439,10 @@ class TypeContext {
2392
2439
  if (type === "string") {
2393
2440
  continue;
2394
2441
  }
2395
- this.discoverTypes(type);
2442
+ this.discoverTypes(type, metadata[field].index, viewTag);
2396
2443
  }
2397
2444
  else if (typeof (fieldType) === "function") {
2398
- this.discoverTypes(fieldType);
2445
+ this.discoverTypes(fieldType, viewTag);
2399
2446
  }
2400
2447
  else {
2401
2448
  const type = Object.values(fieldType)[0];
@@ -2403,11 +2450,13 @@ class TypeContext {
2403
2450
  if (typeof (type) === "string") {
2404
2451
  continue;
2405
2452
  }
2406
- this.discoverTypes(type);
2453
+ this.discoverTypes(type, metadata[field].index, viewTag);
2407
2454
  }
2408
2455
  }
2409
2456
  }
2410
2457
  }
2458
+
2459
+ const DEFAULT_VIEW_TAG = -1;
2411
2460
  /**
2412
2461
  * [See documentation](https://docs.colyseus.io/state/schema/)
2413
2462
  *
@@ -2555,6 +2604,7 @@ function view(tag = DEFAULT_VIEW_TAG) {
2555
2604
  const constructor = target.constructor;
2556
2605
  const parentClass = Object.getPrototypeOf(constructor);
2557
2606
  const parentMetadata = parentClass[Symbol.metadata];
2607
+ // TODO: use Metadata.initialize()
2558
2608
  const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
2559
2609
  if (!metadata[fieldName]) {
2560
2610
  //
@@ -2579,8 +2629,8 @@ function type(type, options) {
2579
2629
  // for inheritance support
2580
2630
  TypeContext.register(constructor);
2581
2631
  const parentClass = Object.getPrototypeOf(constructor);
2582
- const parentMetadata = parentClass[Symbol.metadata];
2583
- const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
2632
+ const parentMetadata = parentClass && parentClass[Symbol.metadata];
2633
+ const metadata = Metadata.initialize(constructor, parentMetadata);
2584
2634
  let fieldIndex;
2585
2635
  /**
2586
2636
  * skip if descriptor already exists for this field (`@deprecated()`)
@@ -2961,13 +3011,13 @@ class Schema {
2961
3011
  }
2962
3012
  return output;
2963
3013
  }
2964
- static debugChangesDeep(ref) {
3014
+ static debugChangesDeep(ref, changeSetName = "changes") {
2965
3015
  let output = "";
2966
3016
  const rootChangeTree = ref[$changes];
2967
3017
  const changeTrees = new Map();
2968
3018
  let totalInstances = 0;
2969
3019
  let totalOperations = 0;
2970
- for (const [changeTree, changes] of (rootChangeTree.root.changes.entries())) {
3020
+ for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
2971
3021
  let includeChangeTree = false;
2972
3022
  let parentChangeTrees = [];
2973
3023
  let parentChangeTree = changeTree.parent?.[$changes];
@@ -3353,6 +3403,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
3353
3403
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
3354
3404
  PERFORMANCE OF THIS SOFTWARE.
3355
3405
  ***************************************************************************** */
3406
+ /* global Reflect, Promise, SuppressedError, Symbol */
3407
+
3356
3408
 
3357
3409
  function __decorate(decorators, target, key, desc) {
3358
3410
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -3366,29 +3418,68 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
3366
3418
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
3367
3419
  };
3368
3420
 
3421
+ class Root {
3422
+ constructor(types) {
3423
+ this.types = types;
3424
+ this.nextUniqueId = 0;
3425
+ this.refCount = new WeakMap();
3426
+ // all changes
3427
+ this.allChanges = new Map();
3428
+ this.allFilteredChanges = new Map();
3429
+ // pending changes to be encoded
3430
+ this.changes = new Map();
3431
+ this.filteredChanges = new Map();
3432
+ }
3433
+ getNextUniqueId() {
3434
+ return this.nextUniqueId++;
3435
+ }
3436
+ add(changeTree) {
3437
+ const refCount = this.refCount.get(changeTree) || 0;
3438
+ this.refCount.set(changeTree, refCount + 1);
3439
+ }
3440
+ remove(changeTree) {
3441
+ const refCount = this.refCount.get(changeTree);
3442
+ if (refCount <= 1) {
3443
+ this.allChanges.delete(changeTree);
3444
+ this.changes.delete(changeTree);
3445
+ if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
3446
+ this.allFilteredChanges.delete(changeTree);
3447
+ this.filteredChanges.delete(changeTree);
3448
+ }
3449
+ this.refCount.delete(changeTree);
3450
+ }
3451
+ else {
3452
+ this.refCount.set(changeTree, refCount - 1);
3453
+ }
3454
+ changeTree.forEachChild((child, _) => this.remove(child));
3455
+ }
3456
+ clear() {
3457
+ this.changes.clear();
3458
+ }
3459
+ }
3460
+
3369
3461
  class Encoder {
3370
3462
  static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
3371
- constructor(root) {
3463
+ constructor(state) {
3372
3464
  this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
3373
- this.setRoot(root);
3374
3465
  //
3375
3466
  // TODO: cache and restore "Context" based on root schema
3376
3467
  // (to avoid creating a new context for every new room)
3377
3468
  //
3378
- this.context = new TypeContext(root.constructor);
3469
+ this.context = new TypeContext(state.constructor);
3470
+ this.root = new Root(this.context);
3471
+ this.setState(state);
3379
3472
  // console.log(">>>>>>>>>>>>>>>> Encoder types");
3380
3473
  // this.context.schemas.forEach((id, schema) => {
3381
3474
  // console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
3382
3475
  // });
3383
3476
  }
3384
- setRoot(state) {
3385
- this.root = new Root();
3477
+ setState(state) {
3386
3478
  this.state = state;
3387
- state[$changes].setRoot(this.root);
3479
+ this.state[$changes].setRoot(this.root);
3388
3480
  }
3389
- encode(it = { offset: 0 }, view, bytes = this.sharedBuffer, changeTrees = this.root.changes) {
3390
- const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
3391
- const isEncodeAll = this.root.allChanges === changeTrees;
3481
+ encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees, initialOffset = it.offset // cache current offset in case we need to resize the buffer
3482
+ ) {
3392
3483
  const hasView = (view !== undefined);
3393
3484
  const rootChangeTree = this.state[$changes];
3394
3485
  const changeTreesIterator = changeTrees.entries();
@@ -3397,6 +3488,12 @@ class Encoder {
3397
3488
  const ctor = ref['constructor'];
3398
3489
  const encoder = ctor[$encoder];
3399
3490
  const filter = ctor[$filter];
3491
+ // try { throw new Error(); } catch (e) {
3492
+ // // only print if not coming from Reflection.ts
3493
+ // if (!e.stack.includes("src/Reflection.ts")) {
3494
+ // console.log("ChangeTree:", { ref: ref.constructor.name, });
3495
+ // }
3496
+ // }
3400
3497
  if (hasView) {
3401
3498
  if (!view.items.has(changeTree)) {
3402
3499
  view.invisible.add(changeTree);
@@ -3407,9 +3504,10 @@ class Encoder {
3407
3504
  }
3408
3505
  }
3409
3506
  // skip root `refId` if it's the first change tree
3410
- if (it.offset !== initialOffset || changeTree !== rootChangeTree) {
3411
- bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3412
- number$1(bytes, changeTree.refId, it);
3507
+ // (unless it "hasView", which will need to revisit the root)
3508
+ if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
3509
+ buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3510
+ number$1(buffer, changeTree.refId, it);
3413
3511
  }
3414
3512
  const changesIterator = changes.entries();
3415
3513
  for (const [fieldIndex, operation] of changesIterator) {
@@ -3421,27 +3519,39 @@ class Encoder {
3421
3519
  // TODO: avoid checking if no view tags were defined
3422
3520
  //
3423
3521
  if (filter && !filter(ref, fieldIndex, view)) {
3424
- // console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
3425
3522
  // console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
3426
3523
  // view?.invisible.add(changeTree);
3427
3524
  continue;
3428
3525
  }
3429
- // console.log("WILL ENCODE", {
3430
- // ref: changeTree.ref.constructor.name,
3431
- // fieldIndex,
3432
- // operation: OPERATION[operation],
3433
- // });
3434
- encoder(this, bytes, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3526
+ // try { throw new Error(); } catch (e) {
3527
+ // // only print if not coming from Reflection.ts
3528
+ // if (!e.stack.includes("src/Reflection.ts")) {
3529
+ // console.log("WILL ENCODE", {
3530
+ // ref: changeTree.ref.constructor.name,
3531
+ // fieldIndex,
3532
+ // operation: OPERATION[operation],
3533
+ // });
3534
+ // }
3535
+ // }
3536
+ encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3435
3537
  }
3436
3538
  }
3437
- if (it.offset > bytes.byteLength) {
3438
- const newSize = getNextPowerOf2(this.sharedBuffer.byteLength * 2);
3439
- console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + bytes.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
3539
+ if (it.offset > buffer.byteLength) {
3540
+ const newSize = getNextPowerOf2(buffer.byteLength * 2);
3541
+ console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
3542
+
3543
+ import { Encoder } from "@colyseus/schema";
3544
+ Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
3545
+ `);
3440
3546
  //
3441
3547
  // resize buffer and re-encode (TODO: can we avoid re-encoding here?)
3442
3548
  //
3443
- this.sharedBuffer = Buffer.allocUnsafeSlow(newSize);
3444
- return this.encode({ offset: initialOffset }, view);
3549
+ buffer = Buffer.allocUnsafeSlow(newSize);
3550
+ // assign resized buffer to local sharedBuffer
3551
+ if (buffer === this.sharedBuffer) {
3552
+ this.sharedBuffer = buffer;
3553
+ }
3554
+ return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
3445
3555
  }
3446
3556
  else {
3447
3557
  //
@@ -3453,42 +3563,47 @@ class Encoder {
3453
3563
  //
3454
3564
  this.onEndEncode(changeTrees);
3455
3565
  }
3456
- // return bytes;
3457
- return bytes.slice(0, it.offset);
3566
+ return buffer.subarray(0, it.offset);
3458
3567
  }
3459
3568
  }
3460
- encodeAll(it = { offset: 0 }) {
3461
- // console.log(`encodeAll(), this.$root.allChanges (${this.$root.allChanges.size})`);
3462
- // Array.from(this.$root.allChanges.entries()).map((item) => {
3463
- // console.log("->", item[0].refId, item[0].ref.toJSON());
3464
- // });
3465
- return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
3569
+ encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
3570
+ // console.log(`\nencodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
3571
+ // this.debugChanges("allChanges");
3572
+ return this.encode(it, undefined, buffer, this.root.allChanges, true);
3466
3573
  }
3467
3574
  encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3468
3575
  const viewOffset = it.offset;
3469
- // console.log(`encodeAllView(), this.$root.allFilteredChanges (${this.$root.allFilteredChanges.size})`);
3470
- // this.debugAllFilteredChanges();
3576
+ // console.log(`\nencodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
3577
+ // this.debugChanges("allFilteredChanges");
3471
3578
  // try to encode "filtered" changes
3472
- this.encode(it, view, bytes, this.root.allFilteredChanges);
3579
+ this.encode(it, view, bytes, this.root.allFilteredChanges, true, viewOffset);
3473
3580
  return Buffer.concat([
3474
- bytes.slice(0, sharedOffset),
3475
- bytes.slice(viewOffset, it.offset)
3581
+ bytes.subarray(0, sharedOffset),
3582
+ bytes.subarray(viewOffset, it.offset)
3476
3583
  ]);
3477
3584
  }
3478
- // debugAllFilteredChanges() {
3479
- // Array.from(this.$root.allFilteredChanges.entries()).map((item) => {
3480
- // console.log("->", { refId: item[0].refId }, item[0].ref.toJSON());
3481
- // if (Array.isArray(item[0].ref.toJSON())) {
3482
- // item[1].forEach((op, key) => {
3483
- // console.log(" ->", { key, op: OPERATION[op] });
3484
- // })
3485
- // }
3486
- // });
3487
- // }
3585
+ debugChanges(field) {
3586
+ const changeSet = (typeof (field) === "string")
3587
+ ? this.root[field]
3588
+ : field;
3589
+ Array.from(changeSet.entries()).map((item) => {
3590
+ const metadata = item[0].ref.constructor[Symbol.metadata];
3591
+ console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
3592
+ item[1].forEach((op, index) => {
3593
+ console.log(" ->", {
3594
+ index,
3595
+ field: metadata?.[index],
3596
+ op: exports.OPERATION[op],
3597
+ });
3598
+ });
3599
+ });
3600
+ }
3488
3601
  encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3489
3602
  const viewOffset = it.offset;
3490
- // try to encode "filtered" changes
3491
- this.encode(it, view, bytes, this.root.filteredChanges);
3603
+ // console.log(`\nencodeView(), view.changes (${view.changes.size})`);
3604
+ // this.debugChanges(view.changes);
3605
+ // console.log(`\nencodeView(), this.root.filteredChanges (${this.root.filteredChanges.size})`);
3606
+ // this.debugChanges("filteredChanges");
3492
3607
  // encode visibility changes (add/remove for this view)
3493
3608
  const viewChangesIterator = view.changes.entries();
3494
3609
  for (const [changeTree, changes] of viewChangesIterator) {
@@ -3515,9 +3630,11 @@ class Encoder {
3515
3630
  //
3516
3631
  // clear "view" changes after encoding
3517
3632
  view.changes.clear();
3633
+ // try to encode "filtered" changes
3634
+ this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
3518
3635
  return Buffer.concat([
3519
- bytes.slice(0, sharedOffset),
3520
- bytes.slice(viewOffset, it.offset)
3636
+ bytes.subarray(0, sharedOffset),
3637
+ bytes.subarray(viewOffset, it.offset)
3521
3638
  ]);
3522
3639
  }
3523
3640
  onEndEncode(changeTrees = this.root.changes) {
@@ -3687,21 +3804,21 @@ class ReferenceTracker {
3687
3804
  class Decoder {
3688
3805
  constructor(root, context) {
3689
3806
  this.currentRefId = 0;
3690
- this.setRoot(root);
3807
+ this.setState(root);
3691
3808
  this.context = context || new TypeContext(root.constructor);
3692
3809
  // console.log(">>>>>>>>>>>>>>>> Decoder types");
3693
3810
  // this.context.schemas.forEach((id, schema) => {
3694
3811
  // console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
3695
3812
  // });
3696
3813
  }
3697
- setRoot(root) {
3814
+ setState(root) {
3698
3815
  this.state = root;
3699
- this.$root = new ReferenceTracker();
3700
- this.$root.addRef(0, root);
3816
+ this.root = new ReferenceTracker();
3817
+ this.root.addRef(0, root);
3701
3818
  }
3702
3819
  decode(bytes, it = { offset: 0 }, ref = this.state) {
3703
3820
  const allChanges = [];
3704
- const $root = this.$root;
3821
+ const $root = this.root;
3705
3822
  const totalBytes = bytes.byteLength;
3706
3823
  let decoder = ref['constructor'][$decoder];
3707
3824
  this.currentRefId = 0;
@@ -3782,7 +3899,7 @@ class Decoder {
3782
3899
  previousValue: value
3783
3900
  });
3784
3901
  if (needRemoveRef) {
3785
- this.$root.removeRef(this.$root.refIds.get(value));
3902
+ this.root.removeRef(this.root.refIds.get(value));
3786
3903
  }
3787
3904
  });
3788
3905
  }
@@ -3822,10 +3939,8 @@ class Reflection extends Schema {
3822
3939
  super(...arguments);
3823
3940
  this.types = new ArraySchema();
3824
3941
  }
3825
- static encode(instance, context) {
3826
- if (!context) {
3827
- context = new TypeContext(instance.constructor);
3828
- }
3942
+ static encode(instance, context, it = { offset: 0 }) {
3943
+ context ??= new TypeContext(instance.constructor);
3829
3944
  const reflection = new Reflection();
3830
3945
  const encoder = new Encoder(reflection);
3831
3946
  const buildType = (currentType, metadata) => {
@@ -3879,7 +3994,6 @@ class Reflection extends Schema {
3879
3994
  }
3880
3995
  buildType(type, klass[Symbol.metadata]);
3881
3996
  }
3882
- const it = { offset: 0 };
3883
3997
  const buf = encoder.encodeAll(it);
3884
3998
  return Buffer.from(buf, 0, it.offset);
3885
3999
  }
@@ -3887,59 +4001,314 @@ class Reflection extends Schema {
3887
4001
  const reflection = new Reflection();
3888
4002
  const reflectionDecoder = new Decoder(reflection);
3889
4003
  reflectionDecoder.decode(bytes, it);
3890
- const context = new TypeContext();
3891
- const schemaTypes = reflection.types.reduce((types, reflectionType) => {
3892
- const parentKlass = types[reflectionType.extendsId] || Schema;
3893
- const schema = class _ extends parentKlass {
4004
+ const typeContext = new TypeContext();
4005
+ // 1st pass, initialize metadata + inheritance
4006
+ reflection.types.forEach((reflectionType) => {
4007
+ const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
4008
+ const schema = class _ extends parentClass {
3894
4009
  };
3895
- // const _metadata = Object.create(_classSuper[Symbol.metadata] ?? null);
3896
- const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
3897
- Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
4010
+ const parentMetadata = parentClass[Symbol.metadata];
3898
4011
  // register for inheritance support
3899
4012
  TypeContext.register(schema);
3900
- const typeid = reflectionType.id;
3901
- types[typeid] = schema;
3902
- context.add(schema, typeid);
3903
- return types;
4013
+ // for inheritance support
4014
+ Metadata.initialize(schema, parentMetadata);
4015
+ typeContext.add(schema, reflectionType.id);
3904
4016
  }, {});
4017
+ // 2nd pass, set fields
3905
4018
  reflection.types.forEach((reflectionType) => {
3906
- const schemaType = schemaTypes[reflectionType.id];
4019
+ const schemaType = typeContext.get(reflectionType.id);
3907
4020
  const metadata = schemaType[Symbol.metadata];
3908
- const parentKlass = reflection.types[reflectionType.extendsId];
3909
- const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
4021
+ // FIXME: use metadata[-1] to get field count
4022
+ const parentFieldIndex = 0;
4023
+ // console.log("--------------------");
4024
+ // // console.log("reflectionType", reflectionType.toJSON());
4025
+ // console.log("reflectionType.fields", reflectionType.fields.toJSON());
4026
+ // console.log("parentFieldIndex", parentFieldIndex);
4027
+ //
4028
+ // FIXME: set fields using parentKlass as well
4029
+ // currently the fields are duplicated on inherited classes
4030
+ //
4031
+ // // const parentKlass = reflection.types[reflectionType.extendsId];
4032
+ // // parentKlass.fields
3910
4033
  reflectionType.fields.forEach((field, i) => {
3911
4034
  const fieldIndex = parentFieldIndex + i;
3912
4035
  if (field.referencedType !== undefined) {
3913
4036
  let fieldType = field.type;
3914
- let refType = schemaTypes[field.referencedType];
4037
+ let refType = typeContext.get(field.referencedType);
3915
4038
  // map or array of primitive type (-1)
3916
4039
  if (!refType) {
3917
4040
  const typeInfo = field.type.split(":");
3918
4041
  fieldType = typeInfo[0];
3919
- refType = typeInfo[1];
4042
+ refType = typeInfo[1]; // string
3920
4043
  }
3921
4044
  if (fieldType === "ref") {
3922
- // type(refType)(schemaType.prototype, field.name);
3923
4045
  Metadata.addField(metadata, fieldIndex, field.name, refType);
3924
4046
  }
3925
4047
  else {
3926
- // type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
3927
4048
  Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
3928
4049
  }
3929
4050
  }
3930
4051
  else {
3931
- // type(field.type as PrimitiveType)(schemaType.prototype, field.name);
3932
4052
  Metadata.addField(metadata, fieldIndex, field.name, field.type);
3933
4053
  }
3934
4054
  });
3935
4055
  });
3936
- return new (schemaTypes[0])();
4056
+ // @ts-ignore
4057
+ return new (typeContext.get(0))();
3937
4058
  }
3938
4059
  }
3939
4060
  __decorate([
3940
4061
  type([ReflectionType])
3941
4062
  ], Reflection.prototype, "types", void 0);
3942
4063
 
4064
+ function getDecoderStateCallbacks(decoder) {
4065
+ const $root = decoder.root;
4066
+ const callbacks = $root.callbacks;
4067
+ const onAddCalls = new WeakMap();
4068
+ let currentOnAddCallback;
4069
+ decoder.triggerChanges = function (allChanges) {
4070
+ const uniqueRefIds = new Set();
4071
+ for (let i = 0, l = allChanges.length; i < l; i++) {
4072
+ const change = allChanges[i];
4073
+ const refId = change.refId;
4074
+ const ref = change.ref;
4075
+ const $callbacks = callbacks[refId];
4076
+ if (!$callbacks) {
4077
+ continue;
4078
+ }
4079
+ //
4080
+ // trigger onRemove on child structure.
4081
+ //
4082
+ if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE &&
4083
+ change.previousValue instanceof Schema) {
4084
+ const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[exports.OPERATION.DELETE];
4085
+ for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
4086
+ deleteCallbacks[i]();
4087
+ }
4088
+ }
4089
+ if (ref instanceof Schema) {
4090
+ //
4091
+ // Handle schema instance
4092
+ //
4093
+ if (!uniqueRefIds.has(refId)) {
4094
+ // trigger onChange
4095
+ const replaceCallbacks = $callbacks?.[exports.OPERATION.REPLACE];
4096
+ for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
4097
+ replaceCallbacks[i]();
4098
+ // try {
4099
+ // } catch (e) {
4100
+ // console.error(e);
4101
+ // }
4102
+ }
4103
+ }
4104
+ if ($callbacks.hasOwnProperty(change.field)) {
4105
+ const fieldCallbacks = $callbacks[change.field];
4106
+ for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
4107
+ fieldCallbacks[i](change.value, change.previousValue);
4108
+ // try {
4109
+ // } catch (e) {
4110
+ // console.error(e);
4111
+ // }
4112
+ }
4113
+ }
4114
+ }
4115
+ else {
4116
+ //
4117
+ // Handle collection of items
4118
+ //
4119
+ if ((change.op & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
4120
+ //
4121
+ // FIXME: `previousValue` should always be available.
4122
+ //
4123
+ if (change.previousValue !== undefined) {
4124
+ // triger onRemove
4125
+ const deleteCallbacks = $callbacks[exports.OPERATION.DELETE];
4126
+ for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
4127
+ deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
4128
+ }
4129
+ }
4130
+ // Handle DELETE_AND_ADD operations
4131
+ if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD) {
4132
+ const addCallbacks = $callbacks[exports.OPERATION.ADD];
4133
+ for (let i = addCallbacks?.length - 1; i >= 0; i--) {
4134
+ addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4135
+ }
4136
+ }
4137
+ }
4138
+ else if ((change.op & exports.OPERATION.ADD) === exports.OPERATION.ADD && change.previousValue === undefined) {
4139
+ // triger onAdd
4140
+ const addCallbacks = $callbacks[exports.OPERATION.ADD];
4141
+ for (let i = addCallbacks?.length - 1; i >= 0; i--) {
4142
+ addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4143
+ }
4144
+ }
4145
+ // trigger onChange
4146
+ if (change.value !== change.previousValue) {
4147
+ const replaceCallbacks = $callbacks[exports.OPERATION.REPLACE];
4148
+ for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
4149
+ replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
4150
+ }
4151
+ }
4152
+ }
4153
+ uniqueRefIds.add(refId);
4154
+ }
4155
+ };
4156
+ function getProxy(metadataOrType, context) {
4157
+ let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
4158
+ let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
4159
+ (metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
4160
+ if (metadata && !isCollection) {
4161
+ const onAddListen = function (ref, prop, callback, immediate) {
4162
+ // immediate trigger
4163
+ if (immediate &&
4164
+ context.instance[prop] !== undefined &&
4165
+ !onAddCalls.has(currentOnAddCallback) // Workaround for https://github.com/colyseus/schema/issues/147
4166
+ ) {
4167
+ callback(context.instance[prop], undefined);
4168
+ }
4169
+ return $root.addCallback($root.refIds.get(ref), prop, callback);
4170
+ };
4171
+ /**
4172
+ * Schema instances
4173
+ */
4174
+ return new Proxy({
4175
+ listen: function listen(prop, callback, immediate = true) {
4176
+ if (context.instance) {
4177
+ return onAddListen(context.instance, prop, callback, immediate);
4178
+ }
4179
+ else {
4180
+ // collection instance not received yet
4181
+ let detachCallback = () => { };
4182
+ context.onInstanceAvailable((ref, existing) => {
4183
+ detachCallback = onAddListen(ref, prop, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
4184
+ });
4185
+ return () => detachCallback();
4186
+ }
4187
+ },
4188
+ onChange: function onChange(callback) {
4189
+ return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, callback);
4190
+ },
4191
+ //
4192
+ // TODO: refactor `bindTo()` implementation.
4193
+ // There is room for improvement.
4194
+ //
4195
+ bindTo: function bindTo(targetObject, properties) {
4196
+ if (!properties) {
4197
+ properties = Object.keys(metadata);
4198
+ }
4199
+ return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, () => {
4200
+ properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
4201
+ });
4202
+ }
4203
+ }, {
4204
+ get(target, prop) {
4205
+ if (metadata[prop]) {
4206
+ const instance = context.instance?.[prop];
4207
+ const onInstanceAvailable = ((callback) => {
4208
+ const unbind = $(context.instance).listen(prop, (value, _) => {
4209
+ callback(value, false);
4210
+ // FIXME: by "unbinding" the callback here,
4211
+ // it will not support when the server
4212
+ // re-instantiates the instance.
4213
+ //
4214
+ unbind?.();
4215
+ }, false);
4216
+ // has existing value
4217
+ if ($root.refIds.get(instance) !== undefined) {
4218
+ callback(instance, true);
4219
+ }
4220
+ });
4221
+ return getProxy(metadata[prop].type, {
4222
+ // make sure refId is available, otherwise need to wait for the instance to be available.
4223
+ instance: ($root.refIds.get(instance) && instance),
4224
+ parentInstance: context.instance,
4225
+ onInstanceAvailable,
4226
+ });
4227
+ }
4228
+ else {
4229
+ // accessing the function
4230
+ return target[prop];
4231
+ }
4232
+ },
4233
+ has(target, prop) { return metadata[prop] !== undefined; },
4234
+ set(_, _1, _2) { throw new Error("not allowed"); },
4235
+ deleteProperty(_, _1) { throw new Error("not allowed"); },
4236
+ });
4237
+ }
4238
+ else {
4239
+ /**
4240
+ * Collection instances
4241
+ */
4242
+ const onAdd = function (ref, callback, immediate) {
4243
+ // Trigger callback on existing items
4244
+ if (immediate) {
4245
+ ref.forEach((v, k) => callback(v, k));
4246
+ }
4247
+ return $root.addCallback($root.refIds.get(ref), exports.OPERATION.ADD, (value, key) => {
4248
+ onAddCalls.set(callback, true);
4249
+ currentOnAddCallback = callback;
4250
+ callback(value, key);
4251
+ onAddCalls.delete(callback);
4252
+ currentOnAddCallback = undefined;
4253
+ });
4254
+ };
4255
+ const onRemove = function (ref, callback) {
4256
+ return $root.addCallback($root.refIds.get(ref), exports.OPERATION.DELETE, callback);
4257
+ };
4258
+ return new Proxy({
4259
+ onAdd: function (callback, immediate = true) {
4260
+ //
4261
+ // https://github.com/colyseus/schema/issues/147
4262
+ // If parent instance has "onAdd" registered, avoid triggering immediate callback.
4263
+ //
4264
+ if (context.instance) {
4265
+ return onAdd(context.instance, callback, immediate && !onAddCalls.has(currentOnAddCallback));
4266
+ }
4267
+ else if (context.onInstanceAvailable) {
4268
+ // collection instance not received yet
4269
+ let detachCallback = () => { };
4270
+ context.onInstanceAvailable((ref, existing) => {
4271
+ detachCallback = onAdd(ref, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
4272
+ });
4273
+ return () => detachCallback();
4274
+ }
4275
+ },
4276
+ onRemove: function (callback) {
4277
+ if (context.onInstanceAvailable) {
4278
+ // collection instance not received yet
4279
+ let detachCallback = () => { };
4280
+ context.onInstanceAvailable((ref) => {
4281
+ detachCallback = onRemove(ref, callback);
4282
+ });
4283
+ return () => detachCallback();
4284
+ }
4285
+ else if (context.instance) {
4286
+ return onRemove(context.instance, callback);
4287
+ }
4288
+ },
4289
+ }, {
4290
+ get(target, prop) {
4291
+ if (!target[prop]) {
4292
+ throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
4293
+ }
4294
+ return target[prop];
4295
+ },
4296
+ has(target, prop) { return target[prop] !== undefined; },
4297
+ set(_, _1, _2) { throw new Error("not allowed"); },
4298
+ deleteProperty(_, _1) { throw new Error("not allowed"); },
4299
+ });
4300
+ }
4301
+ }
4302
+ function $(instance) {
4303
+ return getProxy(undefined, { instance });
4304
+ }
4305
+ return $;
4306
+ }
4307
+
4308
+ function getRawChangesCallback(decoder, callback) {
4309
+ decoder.triggerChanges = callback;
4310
+ }
4311
+
3943
4312
  class StateView {
3944
4313
  constructor() {
3945
4314
  /**
@@ -3957,20 +4326,21 @@ class StateView {
3957
4326
  this.changes = new Map();
3958
4327
  }
3959
4328
  // TODO: allow to set multiple tags at once
3960
- add(obj, tag = DEFAULT_VIEW_TAG) {
4329
+ add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
3961
4330
  if (!obj[$changes]) {
3962
4331
  console.warn("StateView#add(), invalid object:", obj);
3963
4332
  return this;
3964
4333
  }
3965
- let changeTree = obj[$changes];
3966
- this.items.add(changeTree);
3967
- // Add children of this ChangeTree to this view
3968
- changeTree.forEachChild((change, _) => this.add(change.ref, tag));
3969
4334
  // FIXME: ArraySchema/MapSchema does not have metadata
3970
4335
  const metadata = obj.constructor[Symbol.metadata];
3971
- // add parent ChangeTree's, if they are invisible to this view
3972
- // TODO: REFACTOR addParent()
3973
- this.addParent(changeTree, tag);
4336
+ const changeTree = obj[$changes];
4337
+ this.items.add(changeTree);
4338
+ // add parent ChangeTree's
4339
+ // - if it was invisible to this view
4340
+ // - if it were previously filtered out
4341
+ if (checkIncludeParent && changeTree.parent) {
4342
+ this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
4343
+ }
3974
4344
  //
3975
4345
  // TODO: when adding an item of a MapSchema, the changes may not
3976
4346
  // be set (only the parent's changes are set)
@@ -3994,7 +4364,6 @@ class StateView {
3994
4364
  tags = this.tags.get(changeTree);
3995
4365
  }
3996
4366
  tags.add(tag);
3997
- // console.log("BY TAG:", tag);
3998
4367
  // Ref: add tagged properties
3999
4368
  metadata?.[-3]?.[tag]?.forEach((index) => {
4000
4369
  if (changeTree.getChange(index) !== exports.OPERATION.DELETE) {
@@ -4003,73 +4372,63 @@ class StateView {
4003
4372
  });
4004
4373
  }
4005
4374
  else {
4006
- // console.log("DEFAULT TAG", changeTree.allChanges);
4007
- // // add default tag properties
4008
- // metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
4009
- // if (changeTree.getChange(index) !== OPERATION.DELETE) {
4010
- // changes.set(index, OPERATION.ADD);
4011
- // }
4012
- // });
4013
- const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
4375
+ const isInvisible = this.invisible.has(changeTree);
4376
+ const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
4014
4377
  ? changeTree.allFilteredChanges
4015
4378
  : changeTree.allChanges;
4016
- const it = allChangesSet.keys();
4017
- const isInvisible = this.invisible.has(changeTree);
4018
- for (const index of it) {
4019
- if ((isInvisible || metadata?.[metadata?.[index]].tag === tag) &&
4020
- changeTree.getChange(index) !== exports.OPERATION.DELETE) {
4021
- changes.set(index, exports.OPERATION.ADD);
4379
+ changeSet.forEach((op, index) => {
4380
+ const tagAtIndex = metadata?.[metadata?.[index]].tag;
4381
+ if ((isInvisible || // if "invisible", include all
4382
+ tagAtIndex === undefined || // "all change" with no tag
4383
+ tagAtIndex === tag // tagged property
4384
+ ) &&
4385
+ op !== exports.OPERATION.DELETE) {
4386
+ changes.set(index, op);
4022
4387
  }
4023
- }
4024
- }
4025
- // TODO: avoid unnecessary iteration here
4026
- while (changeTree.parent &&
4027
- (changeTree = changeTree.parent[$changes]) &&
4028
- (changeTree.isFiltered || changeTree.isPartiallyFiltered)) {
4029
- this.items.add(changeTree);
4388
+ });
4030
4389
  }
4390
+ // Add children of this ChangeTree to this view
4391
+ changeTree.forEachChild((change, index) => {
4392
+ // Do not ADD children that don't have the same tag
4393
+ if (metadata && metadata[metadata[index]].tag !== tag) {
4394
+ return;
4395
+ }
4396
+ this.add(change.ref, tag, false);
4397
+ });
4031
4398
  return this;
4032
4399
  }
4033
- addParent(changeTree, tag) {
4034
- const parentRef = changeTree.parent;
4035
- if (!parentRef) {
4036
- return;
4400
+ addParent(changeTree, parentIndex, tag) {
4401
+ // view must have all "changeTree" parent tree
4402
+ this.items.add(changeTree);
4403
+ // add parent's parent
4404
+ const parentChangeTree = changeTree.parent?.[$changes];
4405
+ if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
4406
+ this.addParent(parentChangeTree, changeTree.parentIndex, tag);
4037
4407
  }
4038
- const parentChangeTree = parentRef[$changes];
4039
- const parentIndex = changeTree.parentIndex;
4040
- if (!this.invisible.has(parentChangeTree)) {
4041
- // parent is already available, no need to add it!
4408
+ // parent is already available, no need to add it!
4409
+ if (!this.invisible.has(changeTree)) {
4042
4410
  return;
4043
4411
  }
4044
- this.addParent(parentChangeTree, tag);
4045
4412
  // add parent's tag properties
4046
- if (parentChangeTree.getChange(parentIndex) !== exports.OPERATION.DELETE) {
4047
- let parentChanges = this.changes.get(parentChangeTree);
4048
- if (parentChanges === undefined) {
4049
- parentChanges = new Map();
4050
- this.changes.set(parentChangeTree, parentChanges);
4413
+ if (changeTree.getChange(parentIndex) !== exports.OPERATION.DELETE) {
4414
+ let changes = this.changes.get(changeTree);
4415
+ if (changes === undefined) {
4416
+ changes = new Map();
4417
+ this.changes.set(changeTree, changes);
4051
4418
  }
4052
- // console.log("add parent change", {
4053
- // parentIndex,
4054
- // parentChanges,
4055
- // parentChange: (
4056
- // parentChangeTree.getChange(parentIndex) &&
4057
- // OPERATION[parentChangeTree.getChange(parentIndex)]
4058
- // ),
4059
- // })
4060
4419
  if (!this.tags) {
4061
4420
  this.tags = new WeakMap();
4062
4421
  }
4063
4422
  let tags;
4064
- if (!this.tags.has(parentChangeTree)) {
4423
+ if (!this.tags.has(changeTree)) {
4065
4424
  tags = new Set();
4066
- this.tags.set(parentChangeTree, tags);
4425
+ this.tags.set(changeTree, tags);
4067
4426
  }
4068
4427
  else {
4069
- tags = this.tags.get(parentChangeTree);
4428
+ tags = this.tags.get(changeTree);
4070
4429
  }
4071
4430
  tags.add(tag);
4072
- parentChanges.set(parentIndex, exports.OPERATION.ADD);
4431
+ changes.set(parentIndex, exports.OPERATION.ADD);
4073
4432
  }
4074
4433
  }
4075
4434
  remove(obj, tag = DEFAULT_VIEW_TAG) {
@@ -4164,6 +4523,8 @@ exports.dumpChanges = dumpChanges;
4164
4523
  exports.encode = encode;
4165
4524
  exports.encodeKeyValueOperation = encodeArray;
4166
4525
  exports.encodeSchemaOperation = encodeSchemaOperation;
4526
+ exports.getDecoderStateCallbacks = getDecoderStateCallbacks;
4527
+ exports.getRawChangesCallback = getRawChangesCallback;
4167
4528
  exports.registerType = registerType;
4168
4529
  exports.type = type;
4169
4530
  exports.view = view;