@colyseus/schema 2.0.31 → 3.0.0-alpha.0

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 (159) hide show
  1. package/build/cjs/index.js +3614 -2634
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/index.mjs +3324 -2445
  4. package/build/esm/index.mjs.map +1 -1
  5. package/build/umd/index.js +3614 -2634
  6. package/lib/Decoder.d.ts +16 -0
  7. package/lib/Decoder.js +182 -0
  8. package/lib/Decoder.js.map +1 -0
  9. package/lib/Encoder.d.ts +13 -0
  10. package/lib/Encoder.js +79 -0
  11. package/lib/Encoder.js.map +1 -0
  12. package/lib/Metadata.d.ts +36 -0
  13. package/lib/Metadata.js +91 -0
  14. package/lib/Metadata.js.map +1 -0
  15. package/lib/Reflection.d.ts +7 -5
  16. package/lib/Reflection.js +62 -58
  17. package/lib/Reflection.js.map +1 -1
  18. package/lib/Schema.d.ts +39 -51
  19. package/lib/Schema.js +189 -731
  20. package/lib/Schema.js.map +1 -1
  21. package/lib/annotations.d.ts +26 -45
  22. package/lib/annotations.js +363 -194
  23. package/lib/annotations.js.map +1 -1
  24. package/lib/changes/ChangeSet.d.ts +12 -0
  25. package/lib/changes/ChangeSet.js +35 -0
  26. package/lib/changes/ChangeSet.js.map +1 -0
  27. package/lib/changes/DecodeOperation.d.ts +15 -0
  28. package/lib/changes/DecodeOperation.js +186 -0
  29. package/lib/changes/DecodeOperation.js.map +1 -0
  30. package/lib/changes/EncodeOperation.d.ts +18 -0
  31. package/lib/changes/EncodeOperation.js +130 -0
  32. package/lib/changes/EncodeOperation.js.map +1 -0
  33. package/lib/changes/consts.d.ts +14 -0
  34. package/lib/changes/consts.js +18 -0
  35. package/lib/changes/consts.js.map +1 -0
  36. package/lib/decoder/DecodeOperation.d.ts +24 -0
  37. package/lib/decoder/DecodeOperation.js +256 -0
  38. package/lib/decoder/DecodeOperation.js.map +1 -0
  39. package/lib/decoder/Decoder.d.ts +21 -0
  40. package/lib/decoder/Decoder.js +114 -0
  41. package/lib/decoder/Decoder.js.map +1 -0
  42. package/lib/decoder/ReferenceTracker.d.ts +26 -0
  43. package/lib/decoder/ReferenceTracker.js +131 -0
  44. package/lib/decoder/ReferenceTracker.js.map +1 -0
  45. package/lib/decoder/strategy/RawChanges.d.ts +3 -0
  46. package/lib/decoder/strategy/RawChanges.js +8 -0
  47. package/lib/decoder/strategy/RawChanges.js.map +1 -0
  48. package/lib/decoder/strategy/StateCallbacks.d.ts +20 -0
  49. package/lib/decoder/strategy/StateCallbacks.js +240 -0
  50. package/lib/decoder/strategy/StateCallbacks.js.map +1 -0
  51. package/lib/decoding/decode.d.ts +48 -0
  52. package/lib/decoding/decode.js +267 -0
  53. package/lib/decoding/decode.js.map +1 -0
  54. package/lib/ecs.d.ts +11 -0
  55. package/lib/ecs.js +160 -0
  56. package/lib/ecs.js.map +1 -0
  57. package/lib/encoder/ChangeTree.d.ts +72 -0
  58. package/lib/encoder/ChangeTree.js +384 -0
  59. package/lib/encoder/ChangeTree.js.map +1 -0
  60. package/lib/encoder/EncodeOperation.d.ts +25 -0
  61. package/lib/encoder/EncodeOperation.js +156 -0
  62. package/lib/encoder/EncodeOperation.js.map +1 -0
  63. package/lib/encoder/Encoder.d.ts +23 -0
  64. package/lib/encoder/Encoder.js +192 -0
  65. package/lib/encoder/Encoder.js.map +1 -0
  66. package/lib/encoder/StateView.d.ts +21 -0
  67. package/lib/encoder/StateView.js +196 -0
  68. package/lib/encoder/StateView.js.map +1 -0
  69. package/lib/encoding/assert.d.ts +9 -0
  70. package/lib/encoding/assert.js +47 -0
  71. package/lib/encoding/assert.js.map +1 -0
  72. package/lib/encoding/decode.js +1 -1
  73. package/lib/encoding/decode.js.map +1 -1
  74. package/lib/encoding/encode.d.ts +17 -16
  75. package/lib/encoding/encode.js +88 -81
  76. package/lib/encoding/encode.js.map +1 -1
  77. package/lib/encoding/spec.d.ts +25 -0
  78. package/lib/encoding/spec.js +30 -0
  79. package/lib/encoding/spec.js.map +1 -0
  80. package/lib/index.d.ts +18 -10
  81. package/lib/index.js +39 -17
  82. package/lib/index.js.map +1 -1
  83. package/lib/symbol.shim.d.ts +6 -0
  84. package/lib/symbol.shim.js +4 -0
  85. package/lib/symbol.shim.js.map +1 -0
  86. package/lib/types/ArraySchema.js +0 -7
  87. package/lib/types/ArraySchema.js.map +1 -1
  88. package/lib/types/HelperTypes.d.ts +10 -2
  89. package/lib/types/HelperTypes.js.map +1 -1
  90. package/lib/types/custom/ArraySchema.d.ts +245 -0
  91. package/lib/types/custom/ArraySchema.js +659 -0
  92. package/lib/types/custom/ArraySchema.js.map +1 -0
  93. package/lib/types/custom/CollectionSchema.d.ts +42 -0
  94. package/lib/types/custom/CollectionSchema.js +165 -0
  95. package/lib/types/custom/CollectionSchema.js.map +1 -0
  96. package/lib/types/custom/MapSchema.d.ts +43 -0
  97. package/lib/types/custom/MapSchema.js +200 -0
  98. package/lib/types/custom/MapSchema.js.map +1 -0
  99. package/lib/types/custom/SetSchema.d.ts +39 -0
  100. package/lib/types/custom/SetSchema.js +177 -0
  101. package/lib/types/custom/SetSchema.js.map +1 -0
  102. package/lib/types/registry.d.ts +6 -0
  103. package/lib/types/registry.js +19 -0
  104. package/lib/types/registry.js.map +1 -0
  105. package/lib/types/symbols.d.ts +29 -0
  106. package/lib/types/symbols.js +33 -0
  107. package/lib/types/symbols.js.map +1 -0
  108. package/lib/types/utils.d.ts +0 -8
  109. package/lib/types/utils.js +1 -33
  110. package/lib/types/utils.js.map +1 -1
  111. package/lib/usage.d.ts +1 -0
  112. package/lib/usage.js +22 -0
  113. package/lib/usage.js.map +1 -0
  114. package/lib/utils.d.ts +13 -2
  115. package/lib/utils.js +36 -15
  116. package/lib/utils.js.map +1 -1
  117. package/lib/v3.d.ts +1 -0
  118. package/lib/v3.js +427 -0
  119. package/lib/v3.js.map +1 -0
  120. package/lib/v3_bench.d.ts +1 -0
  121. package/lib/v3_bench.js +130 -0
  122. package/lib/v3_bench.js.map +1 -0
  123. package/lib/v3_experiment.d.ts +1 -0
  124. package/lib/v3_experiment.js +407 -0
  125. package/lib/v3_experiment.js.map +1 -0
  126. package/package.json +5 -5
  127. package/src/Metadata.ts +135 -0
  128. package/src/Reflection.ts +75 -66
  129. package/src/Schema.ts +213 -931
  130. package/src/annotations.ts +430 -243
  131. package/src/decoder/DecodeOperation.ts +372 -0
  132. package/src/decoder/Decoder.ts +155 -0
  133. package/src/decoder/ReferenceTracker.ts +151 -0
  134. package/src/decoder/strategy/RawChanges.ts +9 -0
  135. package/src/decoder/strategy/StateCallbacks.ts +326 -0
  136. package/src/encoder/ChangeTree.ts +492 -0
  137. package/src/encoder/EncodeOperation.ts +237 -0
  138. package/src/encoder/Encoder.ts +246 -0
  139. package/src/encoder/StateView.ts +229 -0
  140. package/src/encoding/assert.ts +58 -0
  141. package/src/encoding/decode.ts +1 -1
  142. package/src/encoding/encode.ts +88 -82
  143. package/src/encoding/spec.ts +29 -0
  144. package/src/index.ts +22 -19
  145. package/src/symbol.shim.ts +12 -0
  146. package/src/types/HelperTypes.ts +16 -2
  147. package/src/types/{ArraySchema.ts → custom/ArraySchema.ts} +345 -251
  148. package/src/types/{CollectionSchema.ts → custom/CollectionSchema.ts} +56 -46
  149. package/src/types/{MapSchema.ts → custom/MapSchema.ts} +88 -115
  150. package/src/types/{SetSchema.ts → custom/SetSchema.ts} +58 -47
  151. package/src/types/{typeRegistry.ts → registry.ts} +6 -6
  152. package/src/types/symbols.ts +36 -0
  153. package/src/types/utils.ts +0 -46
  154. package/src/utils.ts +50 -21
  155. package/src/v3_bench.ts +107 -0
  156. package/src/changes/ChangeTree.ts +0 -295
  157. package/src/changes/ReferenceTracker.ts +0 -91
  158. package/src/filters/index.ts +0 -23
  159. package/src/spec.ts +0 -49
package/lib/Schema.js CHANGED
@@ -1,114 +1,104 @@
1
1
  "use strict";
2
+ var _a, _b;
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.Schema = void 0;
4
- const spec_1 = require("./spec");
5
+ const spec_1 = require("./encoding/spec");
5
6
  const annotations_1 = require("./annotations");
6
- const encode = require("./encoding/encode");
7
- const decode = require("./encoding/decode");
8
- const ArraySchema_1 = require("./types/ArraySchema");
9
- const MapSchema_1 = require("./types/MapSchema");
10
- const CollectionSchema_1 = require("./types/CollectionSchema");
11
- const SetSchema_1 = require("./types/SetSchema");
12
- const ChangeTree_1 = require("./changes/ChangeTree");
13
- const filters_1 = require("./filters");
14
- const typeRegistry_1 = require("./types/typeRegistry");
15
- const ReferenceTracker_1 = require("./changes/ReferenceTracker");
16
- const utils_1 = require("./types/utils");
17
- class EncodeSchemaError extends Error {
18
- }
19
- function assertType(value, type, klass, field) {
20
- let typeofTarget;
21
- let allowNull = false;
22
- switch (type) {
23
- case "number":
24
- case "int8":
25
- case "uint8":
26
- case "int16":
27
- case "uint16":
28
- case "int32":
29
- case "uint32":
30
- case "int64":
31
- case "uint64":
32
- case "float32":
33
- case "float64":
34
- typeofTarget = "number";
35
- if (isNaN(value)) {
36
- console.log(`trying to encode "NaN" in ${klass.constructor.name}#${field}`);
37
- }
38
- break;
39
- case "string":
40
- typeofTarget = "string";
41
- allowNull = true;
42
- break;
43
- case "boolean":
44
- // boolean is always encoded as true/false based on truthiness
45
- return;
46
- }
47
- if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) {
48
- let foundValue = `'${JSON.stringify(value)}'${(value && value.constructor && ` (${value.constructor.name})`) || ''}`;
49
- throw new EncodeSchemaError(`a '${typeofTarget}' was expected, but ${foundValue} was provided in ${klass.constructor.name}#${field}`);
50
- }
51
- }
52
- function assertInstanceType(value, type, klass, field) {
53
- if (!(value instanceof type)) {
54
- throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value.constructor.name}' was provided in ${klass.constructor.name}#${field}`);
55
- }
56
- }
57
- function encodePrimitiveType(type, bytes, value, klass, field) {
58
- assertType(value, type, klass, field);
59
- const encodeFunc = encode[type];
60
- if (encodeFunc) {
61
- encodeFunc(bytes, value);
62
- }
63
- else {
64
- throw new EncodeSchemaError(`a '${type}' was expected, but ${value} was provided in ${klass.constructor.name}#${field}`);
65
- }
66
- }
67
- function decodePrimitiveType(type, bytes, it) {
68
- return decode[type](bytes, it);
69
- }
7
+ const ChangeTree_1 = require("./encoder/ChangeTree");
8
+ const symbols_1 = require("./types/symbols");
9
+ const EncodeOperation_1 = require("./encoder/EncodeOperation");
10
+ const DecodeOperation_1 = require("./decoder/DecodeOperation");
11
+ const utils_1 = require("./utils");
70
12
  /**
71
13
  * Schema encoder / decoder
72
14
  */
73
15
  class Schema {
74
- static { this._definition = annotations_1.SchemaDefinition.create(); }
75
- static onError(e) {
76
- console.error(e);
16
+ static { this[_a] = EncodeOperation_1.encodeSchemaOperation; }
17
+ static { this[_b] = DecodeOperation_1.decodeSchemaOperation; }
18
+ /**
19
+ * Assign the property descriptors required to track changes on this instance.
20
+ * @param instance
21
+ */
22
+ static initialize(instance) {
23
+ Object.defineProperty(instance, symbols_1.$changes, {
24
+ value: new ChangeTree_1.ChangeTree(instance),
25
+ enumerable: false,
26
+ writable: true
27
+ });
28
+ const metadata = instance.constructor[Symbol.metadata];
29
+ // Define property descriptors
30
+ for (const field in metadata) {
31
+ if (metadata[field].descriptor) {
32
+ // for encoder
33
+ Object.defineProperty(instance, `_${field}`, {
34
+ value: undefined,
35
+ writable: true,
36
+ enumerable: false,
37
+ configurable: true,
38
+ });
39
+ Object.defineProperty(instance, field, metadata[field].descriptor);
40
+ }
41
+ else {
42
+ // for decoder
43
+ Object.defineProperty(instance, field, {
44
+ value: undefined,
45
+ writable: true,
46
+ enumerable: true,
47
+ configurable: true,
48
+ });
49
+ }
50
+ // Object.defineProperty(instance, field, {
51
+ // ...instance.constructor[Symbol.metadata][field].descriptor
52
+ // });
53
+ // if (args[0]?.hasOwnProperty(field)) {
54
+ // instance[field] = args[0][field];
55
+ // }
56
+ }
77
57
  }
78
58
  static is(type) {
79
- return (type['_definition'] &&
80
- type['_definition'].schema !== undefined);
59
+ return typeof (type[Symbol.metadata]) === "object";
60
+ // const metadata = type[Symbol.metadata];
61
+ // return metadata && Object.prototype.hasOwnProperty.call(metadata, -1);
81
62
  }
82
- onChange(callback) {
83
- return (0, utils_1.addCallback)((this.$callbacks || (this.$callbacks = {})), spec_1.OPERATION.REPLACE, callback);
63
+ /**
64
+ * Track property changes
65
+ */
66
+ static [(_a = symbols_1.$encoder, _b = symbols_1.$decoder, symbols_1.$track)](changeTree, index, operation = spec_1.OPERATION.ADD) {
67
+ changeTree.change(index, operation);
84
68
  }
85
- onRemove(callback) {
86
- return (0, utils_1.addCallback)((this.$callbacks || (this.$callbacks = {})), spec_1.OPERATION.DELETE, callback);
69
+ /**
70
+ * Determine if a property must be filtered.
71
+ * - If returns false, the property is NOT going to be encoded.
72
+ * - If returns true, the property is going to be encoded.
73
+ *
74
+ * Encoding with "filters" happens in two steps:
75
+ * - First, the encoder iterates over all "not owned" properties and encodes them.
76
+ * - Then, the encoder iterates over all "owned" properties per instance and encodes them.
77
+ */
78
+ static [symbols_1.$filter](ref, index, view) {
79
+ const metadata = ref.constructor[Symbol.metadata];
80
+ const tag = metadata[metadata[index]].tag;
81
+ if (view === undefined) {
82
+ // shared pass/encode: encode if doesn't have a tag
83
+ return tag === undefined;
84
+ }
85
+ else if (tag === undefined) {
86
+ // view pass: no tag
87
+ return true;
88
+ }
89
+ else if (tag === annotations_1.DEFAULT_VIEW_TAG) {
90
+ // view pass: default tag
91
+ return view.items.has(ref[symbols_1.$changes]);
92
+ }
93
+ else {
94
+ // view pass: custom tag
95
+ const tags = view.tags?.get(ref[symbols_1.$changes]);
96
+ return tags && tags.has(tag);
97
+ }
87
98
  }
88
99
  // allow inherited classes to have a constructor
89
100
  constructor(...args) {
90
- // fix enumerability of fields for end-user
91
- Object.defineProperties(this, {
92
- $changes: {
93
- value: new ChangeTree_1.ChangeTree(this, undefined, new ReferenceTracker_1.ReferenceTracker()),
94
- enumerable: false,
95
- writable: true
96
- },
97
- // $listeners: {
98
- // value: undefined,
99
- // enumerable: false,
100
- // writable: true
101
- // },
102
- $callbacks: {
103
- value: undefined,
104
- enumerable: false,
105
- writable: true
106
- },
107
- });
108
- const descriptors = this._definition.descriptors;
109
- if (descriptors) {
110
- Object.defineProperties(this, descriptors);
111
- }
101
+ Schema.initialize(this);
112
102
  //
113
103
  // Assign initial values
114
104
  //
@@ -120,7 +110,6 @@ class Schema {
120
110
  Object.assign(this, props);
121
111
  return this;
122
112
  }
123
- get _definition() { return this.constructor._definition; }
124
113
  /**
125
114
  * (Server-side): Flag a property to be encoded for the next patch.
126
115
  * @param instance Schema instance
@@ -128,551 +117,16 @@ class Schema {
128
117
  * @param operation OPERATION to perform (detected automatically)
129
118
  */
130
119
  setDirty(property, operation) {
131
- this.$changes.change(property, operation);
132
- }
133
- /**
134
- * Client-side: listen for changes on property.
135
- * @param prop the property name
136
- * @param callback callback to be triggered on property change
137
- * @param immediate trigger immediatelly if property has been already set.
138
- */
139
- listen(prop, callback, immediate = true) {
140
- if (!this.$callbacks) {
141
- this.$callbacks = {};
142
- }
143
- if (!this.$callbacks[prop]) {
144
- this.$callbacks[prop] = [];
145
- }
146
- this.$callbacks[prop].push(callback);
147
- if (immediate && this[prop] !== undefined) {
148
- callback(this[prop], undefined);
149
- }
150
- // return un-register callback.
151
- return () => (0, utils_1.spliceOne)(this.$callbacks[prop], this.$callbacks[prop].indexOf(callback));
152
- }
153
- decode(bytes, it = { offset: 0 }, ref = this) {
154
- const allChanges = [];
155
- const $root = this.$changes.root;
156
- const totalBytes = bytes.length;
157
- let refId = 0;
158
- $root.refs.set(refId, this);
159
- while (it.offset < totalBytes) {
160
- let byte = bytes[it.offset++];
161
- if (byte == spec_1.SWITCH_TO_STRUCTURE) {
162
- refId = decode.number(bytes, it);
163
- const nextRef = $root.refs.get(refId);
164
- //
165
- // Trying to access a reference that haven't been decoded yet.
166
- //
167
- if (!nextRef) {
168
- throw new Error(`"refId" not found: ${refId}`);
169
- }
170
- ref = nextRef;
171
- continue;
172
- }
173
- const changeTree = ref['$changes'];
174
- const isSchema = (ref['_definition'] !== undefined);
175
- const operation = (isSchema)
176
- ? (byte >> 6) << 6 // "compressed" index + operation
177
- : byte; // "uncompressed" index + operation (array/map items)
178
- if (operation === spec_1.OPERATION.CLEAR) {
179
- //
180
- // TODO: refactor me!
181
- // The `.clear()` method is calling `$root.removeRef(refId)` for
182
- // each item inside this collection
183
- //
184
- ref.clear(allChanges);
185
- continue;
186
- }
187
- const fieldIndex = (isSchema)
188
- ? byte % (operation || 255) // if "REPLACE" operation (0), use 255
189
- : decode.number(bytes, it);
190
- const fieldName = (isSchema)
191
- ? (ref['_definition'].fieldsByIndex[fieldIndex])
192
- : "";
193
- let type = changeTree.getType(fieldIndex);
194
- let value;
195
- let previousValue;
196
- let dynamicIndex;
197
- if (!isSchema) {
198
- previousValue = ref['getByIndex'](fieldIndex);
199
- if ((operation & spec_1.OPERATION.ADD) === spec_1.OPERATION.ADD) { // ADD or DELETE_AND_ADD
200
- dynamicIndex = (ref instanceof MapSchema_1.MapSchema)
201
- ? decode.string(bytes, it)
202
- : fieldIndex;
203
- ref['setIndex'](fieldIndex, dynamicIndex);
204
- }
205
- else {
206
- // here
207
- dynamicIndex = ref['getIndex'](fieldIndex);
208
- }
209
- }
210
- else {
211
- previousValue = ref[`_${fieldName}`];
212
- }
213
- //
214
- // Delete operations
215
- //
216
- if ((operation & spec_1.OPERATION.DELETE) === spec_1.OPERATION.DELETE) {
217
- if (operation !== spec_1.OPERATION.DELETE_AND_ADD) {
218
- ref['deleteByIndex'](fieldIndex);
219
- }
220
- // Flag `refId` for garbage collection.
221
- if (previousValue && previousValue['$changes']) {
222
- $root.removeRef(previousValue['$changes'].refId);
223
- }
224
- value = null;
225
- }
226
- if (fieldName === undefined) {
227
- console.warn("@colyseus/schema: definition mismatch");
228
- //
229
- // keep skipping next bytes until reaches a known structure
230
- // by local decoder.
231
- //
232
- const nextIterator = { offset: it.offset };
233
- while (it.offset < totalBytes) {
234
- if (decode.switchStructureCheck(bytes, it)) {
235
- nextIterator.offset = it.offset + 1;
236
- if ($root.refs.has(decode.number(bytes, nextIterator))) {
237
- break;
238
- }
239
- }
240
- it.offset++;
241
- }
242
- continue;
243
- }
244
- else if (operation === spec_1.OPERATION.DELETE) {
245
- //
246
- // FIXME: refactor me.
247
- // Don't do anything.
248
- //
249
- }
250
- else if (Schema.is(type)) {
251
- const refId = decode.number(bytes, it);
252
- value = $root.refs.get(refId);
253
- if (operation !== spec_1.OPERATION.REPLACE) {
254
- const childType = this.getSchemaType(bytes, it, type);
255
- if (!value) {
256
- value = this.createTypeInstance(childType);
257
- value.$changes.refId = refId;
258
- if (previousValue) {
259
- value.$callbacks = previousValue.$callbacks;
260
- // value.$listeners = previousValue.$listeners;
261
- if (previousValue['$changes'].refId &&
262
- refId !== previousValue['$changes'].refId) {
263
- $root.removeRef(previousValue['$changes'].refId);
264
- }
265
- }
266
- }
267
- $root.addRef(refId, value, (value !== previousValue));
268
- }
269
- }
270
- else if (typeof (type) === "string") {
271
- //
272
- // primitive value (number, string, boolean, etc)
273
- //
274
- value = decodePrimitiveType(type, bytes, it);
275
- }
276
- else {
277
- const typeDef = (0, typeRegistry_1.getType)(Object.keys(type)[0]);
278
- const refId = decode.number(bytes, it);
279
- const valueRef = ($root.refs.has(refId))
280
- ? previousValue || $root.refs.get(refId)
281
- : new typeDef.constructor();
282
- value = valueRef.clone(true);
283
- value.$changes.refId = refId;
284
- // preserve schema callbacks
285
- if (previousValue) {
286
- value['$callbacks'] = previousValue['$callbacks'];
287
- if (previousValue['$changes'].refId &&
288
- refId !== previousValue['$changes'].refId) {
289
- $root.removeRef(previousValue['$changes'].refId);
290
- //
291
- // Trigger onRemove if structure has been replaced.
292
- //
293
- const entries = previousValue.entries();
294
- let iter;
295
- while ((iter = entries.next()) && !iter.done) {
296
- const [key, value] = iter.value;
297
- allChanges.push({
298
- refId,
299
- op: spec_1.OPERATION.DELETE,
300
- field: key,
301
- value: undefined,
302
- previousValue: value,
303
- });
304
- }
305
- }
306
- }
307
- $root.addRef(refId, value, (valueRef !== previousValue));
308
- }
309
- if (value !== null &&
310
- value !== undefined) {
311
- if (value['$changes']) {
312
- value['$changes'].setParent(changeTree.ref, changeTree.root, fieldIndex);
313
- }
314
- if (ref instanceof Schema) {
315
- ref[fieldName] = value;
316
- // ref[`_${fieldName}`] = value;
317
- }
318
- else if (ref instanceof MapSchema_1.MapSchema) {
319
- // const key = ref['$indexes'].get(field);
320
- const key = dynamicIndex;
321
- // ref.set(key, value);
322
- ref['$items'].set(key, value);
323
- ref['$changes'].allChanges.add(fieldIndex);
324
- }
325
- else if (ref instanceof ArraySchema_1.ArraySchema) {
326
- // const key = ref['$indexes'][field];
327
- // console.log("SETTING FOR ArraySchema =>", { field, key, value });
328
- // ref[key] = value;
329
- ref.setAt(fieldIndex, value);
330
- }
331
- else if (ref instanceof CollectionSchema_1.CollectionSchema) {
332
- const index = ref.add(value);
333
- ref['setIndex'](fieldIndex, index);
334
- }
335
- else if (ref instanceof SetSchema_1.SetSchema) {
336
- const index = ref.add(value);
337
- if (index !== false) {
338
- ref['setIndex'](fieldIndex, index);
339
- }
340
- }
341
- }
342
- if (previousValue !== value) {
343
- allChanges.push({
344
- refId,
345
- op: operation,
346
- field: fieldName,
347
- dynamicIndex,
348
- value,
349
- previousValue,
350
- });
351
- }
352
- }
353
- this._triggerChanges(allChanges);
354
- // drop references of unused schemas
355
- $root.garbageCollectDeletedRefs();
356
- return allChanges;
357
- }
358
- encode(encodeAll = false, bytes = [], useFilters = false) {
359
- const rootChangeTree = this.$changes;
360
- const refIdsVisited = new WeakSet();
361
- const changeTrees = [rootChangeTree];
362
- let numChangeTrees = 1;
363
- for (let i = 0; i < numChangeTrees; i++) {
364
- const changeTree = changeTrees[i];
365
- const ref = changeTree.ref;
366
- const isSchema = (ref instanceof Schema);
367
- // Generate unique refId for the ChangeTree.
368
- changeTree.ensureRefId();
369
- // mark this ChangeTree as visited.
370
- refIdsVisited.add(changeTree);
371
- // root `refId` is skipped.
372
- if (changeTree !== rootChangeTree &&
373
- (changeTree.changed || encodeAll)) {
374
- encode.uint8(bytes, spec_1.SWITCH_TO_STRUCTURE);
375
- encode.number(bytes, changeTree.refId);
376
- }
377
- const changes = (encodeAll)
378
- ? Array.from(changeTree.allChanges)
379
- : Array.from(changeTree.changes.values());
380
- for (let j = 0, cl = changes.length; j < cl; j++) {
381
- const operation = (encodeAll)
382
- ? { op: spec_1.OPERATION.ADD, index: changes[j] }
383
- : changes[j];
384
- const fieldIndex = operation.index;
385
- const field = (isSchema)
386
- ? ref['_definition'].fieldsByIndex && ref['_definition'].fieldsByIndex[fieldIndex]
387
- : fieldIndex;
388
- // cache begin index if `useFilters`
389
- const beginIndex = bytes.length;
390
- // encode field index + operation
391
- if (operation.op !== spec_1.OPERATION.TOUCH) {
392
- if (isSchema) {
393
- //
394
- // Compress `fieldIndex` + `operation` into a single byte.
395
- // This adds a limitaion of 64 fields per Schema structure
396
- //
397
- encode.uint8(bytes, (fieldIndex | operation.op));
398
- }
399
- else {
400
- encode.uint8(bytes, operation.op);
401
- // custom operations
402
- if (operation.op === spec_1.OPERATION.CLEAR) {
403
- continue;
404
- }
405
- // indexed operations
406
- encode.number(bytes, fieldIndex);
407
- }
408
- }
409
- //
410
- // encode "alias" for dynamic fields (maps)
411
- //
412
- if (!isSchema &&
413
- (operation.op & spec_1.OPERATION.ADD) == spec_1.OPERATION.ADD // ADD or DELETE_AND_ADD
414
- ) {
415
- if (ref instanceof MapSchema_1.MapSchema) {
416
- //
417
- // MapSchema dynamic key
418
- //
419
- const dynamicIndex = changeTree.ref['$indexes'].get(fieldIndex);
420
- encode.string(bytes, dynamicIndex);
421
- }
422
- }
423
- if (operation.op === spec_1.OPERATION.DELETE) {
424
- //
425
- // TODO: delete from filter cache data.
426
- //
427
- // if (useFilters) {
428
- // delete changeTree.caches[fieldIndex];
429
- // }
430
- continue;
431
- }
432
- // const type = changeTree.childType || ref._schema[field];
433
- const type = changeTree.getType(fieldIndex);
434
- // const type = changeTree.getType(fieldIndex);
435
- const value = changeTree.getValue(fieldIndex);
436
- // Enqueue ChangeTree to be visited
437
- if (value &&
438
- value['$changes'] &&
439
- !refIdsVisited.has(value['$changes'])) {
440
- changeTrees.push(value['$changes']);
441
- value['$changes'].ensureRefId();
442
- numChangeTrees++;
443
- }
444
- if (operation.op === spec_1.OPERATION.TOUCH) {
445
- continue;
446
- }
447
- if (Schema.is(type)) {
448
- assertInstanceType(value, type, ref, field);
449
- //
450
- // Encode refId for this instance.
451
- // The actual instance is going to be encoded on next `changeTree` iteration.
452
- //
453
- encode.number(bytes, value.$changes.refId);
454
- // Try to encode inherited TYPE_ID if it's an ADD operation.
455
- if ((operation.op & spec_1.OPERATION.ADD) === spec_1.OPERATION.ADD) {
456
- this.tryEncodeTypeId(bytes, type, value.constructor);
457
- }
458
- }
459
- else if (typeof (type) === "string") {
460
- //
461
- // Primitive values
462
- //
463
- encodePrimitiveType(type, bytes, value, ref, field);
464
- }
465
- else {
466
- //
467
- // Custom type (MapSchema, ArraySchema, etc)
468
- //
469
- const definition = (0, typeRegistry_1.getType)(Object.keys(type)[0]);
470
- //
471
- // ensure a ArraySchema has been provided
472
- //
473
- assertInstanceType(ref[`_${field}`], definition.constructor, ref, field);
474
- //
475
- // Encode refId for this instance.
476
- // The actual instance is going to be encoded on next `changeTree` iteration.
477
- //
478
- encode.number(bytes, value.$changes.refId);
479
- }
480
- if (useFilters) {
481
- // cache begin / end index
482
- changeTree.cache(fieldIndex, bytes.slice(beginIndex));
483
- }
484
- }
485
- if (!encodeAll && !useFilters) {
486
- changeTree.discard();
487
- }
488
- }
489
- return bytes;
490
- }
491
- encodeAll(useFilters) {
492
- return this.encode(true, [], useFilters);
493
- }
494
- applyFilters(client, encodeAll = false) {
495
- const root = this;
496
- const refIdsDissallowed = new Set();
497
- const $filterState = filters_1.ClientState.get(client);
498
- const changeTrees = [this.$changes];
499
- let numChangeTrees = 1;
500
- let filteredBytes = [];
501
- for (let i = 0; i < numChangeTrees; i++) {
502
- const changeTree = changeTrees[i];
503
- if (refIdsDissallowed.has(changeTree.refId)) {
504
- // console.log("REFID IS NOT ALLOWED. SKIP.", { refId: changeTree.refId })
505
- continue;
506
- }
507
- const ref = changeTree.ref;
508
- const isSchema = ref instanceof Schema;
509
- encode.uint8(filteredBytes, spec_1.SWITCH_TO_STRUCTURE);
510
- encode.number(filteredBytes, changeTree.refId);
511
- const clientHasRefId = $filterState.refIds.has(changeTree);
512
- const isEncodeAll = (encodeAll || !clientHasRefId);
513
- // console.log("REF:", ref.constructor.name);
514
- // console.log("Encode all?", isEncodeAll);
515
- //
516
- // include `changeTree` on list of known refIds by this client.
517
- //
518
- $filterState.addRefId(changeTree);
519
- const containerIndexes = $filterState.containerIndexes.get(changeTree);
520
- const changes = (isEncodeAll)
521
- ? Array.from(changeTree.allChanges)
522
- : Array.from(changeTree.changes.values());
523
- //
524
- // WORKAROUND: tries to re-evaluate previously not included @filter() attributes
525
- // - see "DELETE a field of Schema" test case.
526
- //
527
- if (!encodeAll &&
528
- isSchema &&
529
- ref._definition.indexesWithFilters) {
530
- const indexesWithFilters = ref._definition.indexesWithFilters;
531
- indexesWithFilters.forEach(indexWithFilter => {
532
- if (!containerIndexes.has(indexWithFilter) &&
533
- changeTree.allChanges.has(indexWithFilter)) {
534
- if (isEncodeAll) {
535
- changes.push(indexWithFilter);
536
- }
537
- else {
538
- changes.push({ op: spec_1.OPERATION.ADD, index: indexWithFilter, });
539
- }
540
- }
541
- });
542
- }
543
- for (let j = 0, cl = changes.length; j < cl; j++) {
544
- const change = (isEncodeAll)
545
- ? { op: spec_1.OPERATION.ADD, index: changes[j] }
546
- : changes[j];
547
- // custom operations
548
- if (change.op === spec_1.OPERATION.CLEAR) {
549
- encode.uint8(filteredBytes, change.op);
550
- continue;
551
- }
552
- const fieldIndex = change.index;
553
- //
554
- // Deleting fields: encode the operation + field index
555
- //
556
- if (change.op === spec_1.OPERATION.DELETE) {
557
- //
558
- // DELETE operations also need to go through filtering.
559
- //
560
- // TODO: cache the previous value so we can access the value (primitive or `refId`)
561
- // (check against `$filterState.refIds`)
562
- //
563
- if (isSchema) {
564
- encode.uint8(filteredBytes, change.op | fieldIndex);
565
- }
566
- else {
567
- encode.uint8(filteredBytes, change.op);
568
- encode.number(filteredBytes, fieldIndex);
569
- }
570
- continue;
571
- }
572
- // indexed operation
573
- const value = changeTree.getValue(fieldIndex);
574
- const type = changeTree.getType(fieldIndex);
575
- if (isSchema) {
576
- // Is a Schema!
577
- const filter = (ref._definition.filters &&
578
- ref._definition.filters[fieldIndex]);
579
- if (filter && !filter.call(ref, client, value, root)) {
580
- if (value && value['$changes']) {
581
- refIdsDissallowed.add(value['$changes'].refId);
582
- ;
583
- }
584
- continue;
585
- }
586
- }
587
- else {
588
- // Is a collection! (map, array, etc.)
589
- const parent = changeTree.parent;
590
- const filter = changeTree.getChildrenFilter();
591
- if (filter && !filter.call(parent, client, ref['$indexes'].get(fieldIndex), value, root)) {
592
- if (value && value['$changes']) {
593
- refIdsDissallowed.add(value['$changes'].refId);
594
- }
595
- continue;
596
- }
597
- }
598
- // visit child ChangeTree on further iteration.
599
- if (value['$changes']) {
600
- changeTrees.push(value['$changes']);
601
- numChangeTrees++;
602
- }
603
- //
604
- // Copy cached bytes
605
- //
606
- if (change.op !== spec_1.OPERATION.TOUCH) {
607
- //
608
- // TODO: refactor me!
609
- //
610
- if (change.op === spec_1.OPERATION.ADD || isSchema) {
611
- //
612
- // use cached bytes directly if is from Schema type.
613
- //
614
- filteredBytes.push.apply(filteredBytes, changeTree.caches[fieldIndex] ?? []);
615
- containerIndexes.add(fieldIndex);
616
- }
617
- else {
618
- if (containerIndexes.has(fieldIndex)) {
619
- //
620
- // use cached bytes if already has the field
621
- //
622
- filteredBytes.push.apply(filteredBytes, changeTree.caches[fieldIndex] ?? []);
623
- }
624
- else {
625
- //
626
- // force ADD operation if field is not known by this client.
627
- //
628
- containerIndexes.add(fieldIndex);
629
- encode.uint8(filteredBytes, spec_1.OPERATION.ADD);
630
- encode.number(filteredBytes, fieldIndex);
631
- if (ref instanceof MapSchema_1.MapSchema) {
632
- //
633
- // MapSchema dynamic key
634
- //
635
- const dynamicIndex = changeTree.ref['$indexes'].get(fieldIndex);
636
- encode.string(filteredBytes, dynamicIndex);
637
- }
638
- if (value['$changes']) {
639
- encode.number(filteredBytes, value['$changes'].refId);
640
- }
641
- else {
642
- // "encodePrimitiveType" without type checking.
643
- // the type checking has been done on the first .encode() call.
644
- encode[type](filteredBytes, value);
645
- }
646
- }
647
- }
648
- }
649
- else if (value['$changes'] && !isSchema) {
650
- //
651
- // TODO:
652
- // - track ADD/REPLACE/DELETE instances on `$filterState`
653
- // - do NOT always encode dynamicIndex for MapSchema.
654
- // (If client already has that key, only the first index is necessary.)
655
- //
656
- encode.uint8(filteredBytes, spec_1.OPERATION.ADD);
657
- encode.number(filteredBytes, fieldIndex);
658
- if (ref instanceof MapSchema_1.MapSchema) {
659
- //
660
- // MapSchema dynamic key
661
- //
662
- const dynamicIndex = changeTree.ref['$indexes'].get(fieldIndex);
663
- encode.string(filteredBytes, dynamicIndex);
664
- }
665
- encode.number(filteredBytes, value['$changes'].refId);
666
- }
667
- }
668
- ;
669
- }
670
- return filteredBytes;
120
+ this[symbols_1.$changes].change(this.constructor[Symbol.metadata][property].index, operation);
671
121
  }
672
122
  clone() {
673
123
  const cloned = new (this.constructor);
674
- const schema = this._definition.schema;
675
- for (let field in schema) {
124
+ const metadata = this.constructor[Symbol.metadata];
125
+ //
126
+ // TODO: clone all properties, not only annotated ones
127
+ //
128
+ // for (const field in this) {
129
+ for (const field in metadata) {
676
130
  if (typeof (this[field]) === "object" &&
677
131
  typeof (this[field]?.clone) === "function") {
678
132
  // deep clone
@@ -686,116 +140,120 @@ class Schema {
686
140
  return cloned;
687
141
  }
688
142
  toJSON() {
689
- const schema = this._definition.schema;
690
- const deprecated = this._definition.deprecated;
143
+ const metadata = this.constructor[Symbol.metadata];
691
144
  const obj = {};
692
- for (let field in schema) {
693
- if (!deprecated[field] && this[field] !== null && typeof (this[field]) !== "undefined") {
694
- obj[field] = (typeof (this[field]['toJSON']) === "function")
695
- ? this[field]['toJSON']()
696
- : this[`_${field}`];
145
+ for (const fieldName in metadata) {
146
+ const field = metadata[fieldName];
147
+ if (!field.deprecated && this[fieldName] !== null && typeof (this[fieldName]) !== "undefined") {
148
+ obj[fieldName] = (typeof (this[fieldName]['toJSON']) === "function")
149
+ ? this[fieldName]['toJSON']()
150
+ : this[fieldName];
697
151
  }
698
152
  }
699
153
  return obj;
700
154
  }
701
155
  discardAllChanges() {
702
- this.$changes.discardAll();
156
+ this[symbols_1.$changes].discardAll();
703
157
  }
704
- getByIndex(index) {
705
- return this[this._definition.fieldsByIndex[index]];
158
+ [symbols_1.$getByIndex](index) {
159
+ return this[this.constructor[Symbol.metadata][index]];
706
160
  }
707
- deleteByIndex(index) {
708
- this[this._definition.fieldsByIndex[index]] = undefined;
161
+ [symbols_1.$deleteByIndex](index) {
162
+ this[this.constructor[Symbol.metadata][index]] = undefined;
709
163
  }
710
- tryEncodeTypeId(bytes, type, targetType) {
711
- if (type._typeid !== targetType._typeid) {
712
- encode.uint8(bytes, spec_1.TYPE_ID);
713
- encode.number(bytes, targetType._typeid);
714
- }
164
+ static debugRefIds(instance, jsonContents = true, level = 0) {
165
+ const ref = instance;
166
+ const changeTree = ref[symbols_1.$changes];
167
+ const contents = (jsonContents) ? ` - ${JSON.stringify(ref.toJSON())}` : "";
168
+ let output = "";
169
+ output += `${(0, utils_1.getIndent)(level)}${ref.constructor.name} (${ref[symbols_1.$changes].refId})${contents}\n`;
170
+ changeTree.forEachChild((childChangeTree) => output += this.debugRefIds(childChangeTree.ref, jsonContents, level + 1));
171
+ return output;
715
172
  }
716
- getSchemaType(bytes, it, defaultType) {
717
- let type;
718
- if (bytes[it.offset] === spec_1.TYPE_ID) {
719
- it.offset++;
720
- type = this.constructor._context.get(decode.number(bytes, it));
173
+ /**
174
+ * Return a string representation of the changes on a Schema instance.
175
+ * The list of changes is cleared after each encode.
176
+ *
177
+ * @param instance Schema instance
178
+ * @param isEncodeAll Return "full encode" instead of current change set.
179
+ * @returns
180
+ */
181
+ static debugChanges(instance, isEncodeAll = false) {
182
+ const changeTree = instance[symbols_1.$changes];
183
+ const changeSet = (isEncodeAll) ? changeTree.allChanges : changeTree.changes;
184
+ const changeSetName = (isEncodeAll) ? "allChanges" : "changes";
185
+ let output = `${instance.constructor.name} (${changeTree.refId}) -> .${changeSetName}:\n`;
186
+ function dumpChangeSet(changeSet) {
187
+ Array.from(changeSet)
188
+ .sort((a, b) => a[0] - b[0])
189
+ .forEach(([index, operation]) => output += `- [${index}]: ${spec_1.OPERATION[operation]} (${JSON.stringify(changeTree.getValue(index, isEncodeAll))})\n`);
721
190
  }
722
- return type || defaultType;
723
- }
724
- createTypeInstance(type) {
725
- let instance = new type();
726
- // assign root on $changes
727
- instance.$changes.root = this.$changes.root;
728
- return instance;
191
+ dumpChangeSet(changeSet);
192
+ // display filtered changes
193
+ if (!isEncodeAll && changeTree.filteredChanges?.size > 0) {
194
+ output += `${instance.constructor.name} (${changeTree.refId}) -> .filteredChanges:\n`;
195
+ dumpChangeSet(changeTree.filteredChanges);
196
+ }
197
+ // display filtered changes
198
+ if (isEncodeAll && changeTree.allFilteredChanges?.size > 0) {
199
+ output += `${instance.constructor.name} (${changeTree.refId}) -> .allFilteredChanges:\n`;
200
+ dumpChangeSet(changeTree.allFilteredChanges);
201
+ }
202
+ return output;
729
203
  }
730
- _triggerChanges(changes) {
731
- const uniqueRefIds = new Set();
732
- const $refs = this.$changes.root.refs;
733
- for (let i = 0; i < changes.length; i++) {
734
- const change = changes[i];
735
- const refId = change.refId;
736
- const ref = $refs.get(refId);
737
- const $callbacks = ref['$callbacks'];
738
- //
739
- // trigger onRemove on child structure.
740
- //
741
- if ((change.op & spec_1.OPERATION.DELETE) === spec_1.OPERATION.DELETE &&
742
- change.previousValue instanceof Schema) {
743
- change.previousValue['$callbacks']?.[spec_1.OPERATION.DELETE]?.forEach(callback => callback());
744
- }
745
- // no callbacks defined, skip this structure!
746
- if (!$callbacks) {
747
- continue;
748
- }
749
- if (ref instanceof Schema) {
750
- if (!uniqueRefIds.has(refId)) {
751
- try {
752
- // trigger onChange
753
- $callbacks?.[spec_1.OPERATION.REPLACE]?.forEach(callback => callback());
754
- }
755
- catch (e) {
756
- Schema.onError(e);
757
- }
758
- }
759
- try {
760
- if ($callbacks.hasOwnProperty(change.field)) {
761
- $callbacks[change.field]?.forEach((callback) => callback(change.value, change.previousValue));
762
- }
763
- }
764
- catch (e) {
765
- Schema.onError(e);
766
- }
204
+ static debugChangesDeep(ref) {
205
+ let output = "";
206
+ const rootChangeTree = ref[symbols_1.$changes];
207
+ const changeTrees = new Map();
208
+ let totalInstances = 0;
209
+ let totalOperations = 0;
210
+ for (const [changeTree, changes] of (rootChangeTree.root.changes.entries())) {
211
+ let includeChangeTree = false;
212
+ let parentChangeTrees = [];
213
+ let parentChangeTree = changeTree.parent?.[symbols_1.$changes];
214
+ if (changeTree === rootChangeTree) {
215
+ includeChangeTree = true;
767
216
  }
768
217
  else {
769
- // is a collection of items
770
- if (change.op === spec_1.OPERATION.ADD && change.previousValue === undefined) {
771
- // triger onAdd
772
- $callbacks[spec_1.OPERATION.ADD]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
773
- }
774
- else if (change.op === spec_1.OPERATION.DELETE) {
775
- //
776
- // FIXME: `previousValue` should always be available.
777
- // ADD + DELETE operations are still encoding DELETE operation.
778
- //
779
- if (change.previousValue !== undefined) {
780
- // triger onRemove
781
- $callbacks[spec_1.OPERATION.DELETE]?.forEach(callback => callback(change.previousValue, change.dynamicIndex ?? change.field));
782
- }
783
- }
784
- else if (change.op === spec_1.OPERATION.DELETE_AND_ADD) {
785
- // triger onRemove
786
- if (change.previousValue !== undefined) {
787
- $callbacks[spec_1.OPERATION.DELETE]?.forEach(callback => callback(change.previousValue, change.dynamicIndex ?? change.field));
218
+ while (parentChangeTree !== undefined) {
219
+ parentChangeTrees.push(parentChangeTree);
220
+ if (parentChangeTree.ref === ref) {
221
+ includeChangeTree = true;
222
+ break;
788
223
  }
789
- // triger onAdd
790
- $callbacks[spec_1.OPERATION.ADD]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
791
- }
792
- // trigger onChange
793
- if (change.value !== change.previousValue) {
794
- $callbacks[spec_1.OPERATION.REPLACE]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
224
+ parentChangeTree = parentChangeTree.parent?.[symbols_1.$changes];
795
225
  }
796
226
  }
797
- uniqueRefIds.add(refId);
227
+ if (includeChangeTree) {
228
+ totalInstances += 1;
229
+ totalOperations += changes.size;
230
+ changeTrees.set(changeTree, parentChangeTrees.reverse());
231
+ }
232
+ }
233
+ output += "---\n";
234
+ output += `root refId: ${rootChangeTree.refId}\n`;
235
+ output += `Total instances: ${totalInstances}\n`;
236
+ output += `Total changes: ${totalOperations}\n`;
237
+ output += "---\n";
238
+ // based on root.changes, display a tree of changes that has the "ref" instance as parent
239
+ const visitedParents = new WeakSet();
240
+ for (const [changeTree, parentChangeTrees] of changeTrees.entries()) {
241
+ parentChangeTrees.forEach((parentChangeTree, level) => {
242
+ if (!visitedParents.has(parentChangeTree)) {
243
+ output += `${(0, utils_1.getIndent)(level)}${parentChangeTree.ref.constructor.name} (refId: ${parentChangeTree.refId})\n`;
244
+ visitedParents.add(parentChangeTree);
245
+ }
246
+ });
247
+ const changes = changeTree.changes;
248
+ const level = parentChangeTrees.length;
249
+ const indent = (0, utils_1.getIndent)(level);
250
+ const parentIndex = (level > 0) ? `(${changeTree.parentIndex}) ` : "";
251
+ output += `${indent}${parentIndex}${changeTree.ref.constructor.name} (refId: ${changeTree.refId}) - changes: ${changes.size}\n`;
252
+ for (const [index, operation] of changes) {
253
+ output += `${(0, utils_1.getIndent)(level + 1)}${spec_1.OPERATION[operation]}: ${index}\n`;
254
+ }
798
255
  }
256
+ return `${output}`;
799
257
  }
800
258
  }
801
259
  exports.Schema = Schema;