@colyseus/schema 1.1.0-alpha.2 → 2.0.3
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.
- package/README.md +1 -4
- package/build/cjs/index.js +1197 -1001
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +353 -311
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +374 -335
- package/lib/Reflection.js +23 -13
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +26 -18
- package/lib/Schema.js +121 -165
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +26 -8
- package/lib/annotations.js +62 -14
- package/lib/annotations.js.map +1 -1
- package/lib/changes/ChangeTree.d.ts +4 -16
- package/lib/changes/ChangeTree.js +1 -72
- package/lib/changes/ChangeTree.js.map +1 -1
- package/lib/changes/ReferenceTracker.d.ts +14 -0
- package/lib/changes/ReferenceTracker.js +77 -0
- package/lib/changes/ReferenceTracker.js.map +1 -0
- package/lib/codegen/languages/csharp.js +39 -17
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/ts.js +11 -2
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +3 -1
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +14 -1
- package/lib/codegen/types.js.map +1 -1
- package/lib/filters/index.d.ts +2 -2
- package/lib/filters/index.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +6 -6
- package/lib/index.js.map +1 -1
- package/lib/types/ArraySchema.d.ts +8 -5
- package/lib/types/ArraySchema.js +22 -19
- package/lib/types/ArraySchema.js.map +1 -1
- package/lib/types/CollectionSchema.d.ts +8 -5
- package/lib/types/CollectionSchema.js +17 -11
- package/lib/types/CollectionSchema.js.map +1 -1
- package/lib/types/MapSchema.d.ts +8 -5
- package/lib/types/MapSchema.js +20 -11
- package/lib/types/MapSchema.js.map +1 -1
- package/lib/types/SetSchema.d.ts +8 -5
- package/lib/types/SetSchema.js +22 -14
- package/lib/types/SetSchema.js.map +1 -1
- package/lib/types/typeRegistry.d.ts +5 -0
- package/lib/types/typeRegistry.js +13 -0
- package/lib/types/typeRegistry.js.map +1 -0
- package/lib/types/utils.d.ts +9 -0
- package/lib/types/utils.js +50 -0
- package/lib/types/utils.js.map +1 -0
- package/package.json +3 -8
package/build/esm/index.mjs
CHANGED
|
@@ -37,73 +37,6 @@ var OPERATION;
|
|
|
37
37
|
// CLEAR = 10,
|
|
38
38
|
// }
|
|
39
39
|
|
|
40
|
-
//
|
|
41
|
-
// Root holds all schema references by unique id
|
|
42
|
-
//
|
|
43
|
-
class Root {
|
|
44
|
-
constructor() {
|
|
45
|
-
//
|
|
46
|
-
// Relation of refId => Schema structure
|
|
47
|
-
// For direct access of structures during decoding time.
|
|
48
|
-
//
|
|
49
|
-
this.refs = new Map();
|
|
50
|
-
this.refCounts = {};
|
|
51
|
-
this.deletedRefs = new Set();
|
|
52
|
-
this.nextUniqueId = 0;
|
|
53
|
-
}
|
|
54
|
-
getNextUniqueId() {
|
|
55
|
-
return this.nextUniqueId++;
|
|
56
|
-
}
|
|
57
|
-
// for decoding
|
|
58
|
-
addRef(refId, ref, incrementCount = true) {
|
|
59
|
-
this.refs.set(refId, ref);
|
|
60
|
-
if (incrementCount) {
|
|
61
|
-
this.refCounts[refId] = (this.refCounts[refId] || 0) + 1;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// for decoding
|
|
65
|
-
removeRef(refId) {
|
|
66
|
-
this.refCounts[refId] = this.refCounts[refId] - 1;
|
|
67
|
-
this.deletedRefs.add(refId);
|
|
68
|
-
}
|
|
69
|
-
clearRefs() {
|
|
70
|
-
this.refs.clear();
|
|
71
|
-
this.deletedRefs.clear();
|
|
72
|
-
this.refCounts = {};
|
|
73
|
-
}
|
|
74
|
-
// for decoding
|
|
75
|
-
garbageCollectDeletedRefs() {
|
|
76
|
-
this.deletedRefs.forEach((refId) => {
|
|
77
|
-
if (this.refCounts[refId] <= 0) {
|
|
78
|
-
const ref = this.refs.get(refId);
|
|
79
|
-
//
|
|
80
|
-
// Ensure child schema instances have their references removed as well.
|
|
81
|
-
//
|
|
82
|
-
if (ref instanceof Schema) {
|
|
83
|
-
for (const fieldName in ref['_definition'].schema) {
|
|
84
|
-
if (typeof (ref['_definition'].schema[fieldName]) !== "string" &&
|
|
85
|
-
ref[fieldName] &&
|
|
86
|
-
ref[fieldName]['$changes']) {
|
|
87
|
-
this.removeRef(ref[fieldName]['$changes'].refId);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
const definition = ref['$changes'].parent._definition;
|
|
93
|
-
const type = definition.schema[definition.fieldsByIndex[ref['$changes'].parentIndex]];
|
|
94
|
-
if (typeof (Object.values(type)[0]) === "function") {
|
|
95
|
-
Array.from(ref.values())
|
|
96
|
-
.forEach((child) => this.removeRef(child['$changes'].refId));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
this.refs.delete(refId);
|
|
100
|
-
delete this.refCounts[refId];
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
// clear deleted refs.
|
|
104
|
-
this.deletedRefs.clear();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
40
|
class ChangeTree {
|
|
108
41
|
constructor(ref, parent, root) {
|
|
109
42
|
this.changed = false;
|
|
@@ -300,13 +233,48 @@ class ChangeTree {
|
|
|
300
233
|
}
|
|
301
234
|
}
|
|
302
235
|
|
|
303
|
-
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
//
|
|
236
|
+
function addCallback($callbacks, op, callback, existing) {
|
|
237
|
+
// initialize list of callbacks
|
|
238
|
+
if (!$callbacks[op]) {
|
|
239
|
+
$callbacks[op] = [];
|
|
240
|
+
}
|
|
241
|
+
$callbacks[op].push(callback);
|
|
242
|
+
//
|
|
243
|
+
// Trigger callback for existing elements
|
|
244
|
+
// - OPERATION.ADD
|
|
245
|
+
// - OPERATION.REPLACE
|
|
246
|
+
//
|
|
247
|
+
existing?.forEach((item, key) => callback(item, key));
|
|
248
|
+
return () => spliceOne($callbacks[op], $callbacks[op].indexOf(callback));
|
|
249
|
+
}
|
|
250
|
+
function removeChildRefs(changes) {
|
|
251
|
+
const needRemoveRef = (typeof (this.$changes.getType()) !== "string");
|
|
252
|
+
this.$items.forEach((item, key) => {
|
|
253
|
+
changes.push({
|
|
254
|
+
refId: this.$changes.refId,
|
|
255
|
+
op: OPERATION.DELETE,
|
|
256
|
+
field: key,
|
|
257
|
+
value: undefined,
|
|
258
|
+
previousValue: item
|
|
259
|
+
});
|
|
260
|
+
if (needRemoveRef) {
|
|
261
|
+
this.$changes.root.removeRef(item['$changes'].refId);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function spliceOne(arr, index) {
|
|
266
|
+
// manually splice an array
|
|
267
|
+
if (index === -1 || index >= arr.length) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
const len = arr.length - 1;
|
|
271
|
+
for (let i = index; i < len; i++) {
|
|
272
|
+
arr[i] = arr[i + 1];
|
|
273
|
+
}
|
|
274
|
+
arr.length = len;
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
310
278
|
const DEFAULT_SORT = (a, b) => {
|
|
311
279
|
const A = a.toString();
|
|
312
280
|
const B = b.toString();
|
|
@@ -373,8 +341,19 @@ class ArraySchema {
|
|
|
373
341
|
this.$refId = 0;
|
|
374
342
|
this.push.apply(this, items);
|
|
375
343
|
}
|
|
344
|
+
onAdd(callback, triggerAll = true) {
|
|
345
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.ADD, callback, (triggerAll)
|
|
346
|
+
? this.$items
|
|
347
|
+
: undefined);
|
|
348
|
+
}
|
|
349
|
+
onRemove(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
|
|
350
|
+
onChange(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
|
|
376
351
|
static is(type) {
|
|
377
|
-
return
|
|
352
|
+
return (
|
|
353
|
+
// type format: ["string"]
|
|
354
|
+
Array.isArray(type) ||
|
|
355
|
+
// type format: { array: "string" }
|
|
356
|
+
(type['array'] !== undefined));
|
|
378
357
|
}
|
|
379
358
|
set length(value) {
|
|
380
359
|
if (value === 0) {
|
|
@@ -440,17 +419,19 @@ class ArraySchema {
|
|
|
440
419
|
this.$indexes.delete(index);
|
|
441
420
|
return this.$items.delete(index);
|
|
442
421
|
}
|
|
443
|
-
clear(
|
|
422
|
+
clear(changes) {
|
|
444
423
|
// discard previous operations.
|
|
445
424
|
this.$changes.discard(true, true);
|
|
446
425
|
this.$changes.indexes = {};
|
|
447
426
|
// clear previous indexes
|
|
448
427
|
this.$indexes.clear();
|
|
449
|
-
//
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
428
|
+
//
|
|
429
|
+
// When decoding:
|
|
430
|
+
// - enqueue items for DELETE callback.
|
|
431
|
+
// - flag child items for garbage collection.
|
|
432
|
+
//
|
|
433
|
+
if (changes) {
|
|
434
|
+
removeChildRefs.call(this, changes);
|
|
454
435
|
}
|
|
455
436
|
// clear items
|
|
456
437
|
this.$items.clear();
|
|
@@ -784,9 +765,6 @@ class ArraySchema {
|
|
|
784
765
|
return cloned;
|
|
785
766
|
}
|
|
786
767
|
;
|
|
787
|
-
triggerAll() {
|
|
788
|
-
Schema.prototype.triggerAll.apply(this);
|
|
789
|
-
}
|
|
790
768
|
}
|
|
791
769
|
|
|
792
770
|
function getMapProxy(value) {
|
|
@@ -838,6 +816,13 @@ class MapSchema {
|
|
|
838
816
|
}
|
|
839
817
|
}
|
|
840
818
|
}
|
|
819
|
+
onAdd(callback, triggerAll = true) {
|
|
820
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.ADD, callback, (triggerAll)
|
|
821
|
+
? this.$items
|
|
822
|
+
: undefined);
|
|
823
|
+
}
|
|
824
|
+
onRemove(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
|
|
825
|
+
onChange(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
|
|
841
826
|
static is(type) {
|
|
842
827
|
return type['map'] !== undefined;
|
|
843
828
|
}
|
|
@@ -845,6 +830,9 @@ class MapSchema {
|
|
|
845
830
|
[Symbol.iterator]() { return this.$items[Symbol.iterator](); }
|
|
846
831
|
get [Symbol.toStringTag]() { return this.$items[Symbol.toStringTag]; }
|
|
847
832
|
set(key, value) {
|
|
833
|
+
if (value === undefined || value === null) {
|
|
834
|
+
throw new Error(`MapSchema#set('${key}', ${value}): trying to set ${value} value on '${key}'.`);
|
|
835
|
+
}
|
|
848
836
|
// get "index" for this value.
|
|
849
837
|
const hasIndex = typeof (this.$changes.indexes[key]) !== "undefined";
|
|
850
838
|
const index = (hasIndex)
|
|
@@ -888,17 +876,19 @@ class MapSchema {
|
|
|
888
876
|
this.$changes.delete(key);
|
|
889
877
|
return this.$items.delete(key);
|
|
890
878
|
}
|
|
891
|
-
clear(
|
|
879
|
+
clear(changes) {
|
|
892
880
|
// discard previous operations.
|
|
893
881
|
this.$changes.discard(true, true);
|
|
894
882
|
this.$changes.indexes = {};
|
|
895
883
|
// clear previous indexes
|
|
896
884
|
this.$indexes.clear();
|
|
897
|
-
//
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
885
|
+
//
|
|
886
|
+
// When decoding:
|
|
887
|
+
// - enqueue items for DELETE callback.
|
|
888
|
+
// - flag child items for garbage collection.
|
|
889
|
+
//
|
|
890
|
+
if (changes) {
|
|
891
|
+
removeChildRefs.call(this, changes);
|
|
902
892
|
}
|
|
903
893
|
// clear items
|
|
904
894
|
this.$items.clear();
|
|
@@ -970,9 +960,6 @@ class MapSchema {
|
|
|
970
960
|
}
|
|
971
961
|
return cloned;
|
|
972
962
|
}
|
|
973
|
-
triggerAll() {
|
|
974
|
-
Schema.prototype.triggerAll.apply(this);
|
|
975
|
-
}
|
|
976
963
|
}
|
|
977
964
|
|
|
978
965
|
const registeredTypes = {};
|
|
@@ -1011,6 +998,9 @@ class SchemaDefinition {
|
|
|
1011
998
|
? { array: type[0] }
|
|
1012
999
|
: type;
|
|
1013
1000
|
}
|
|
1001
|
+
hasField(field) {
|
|
1002
|
+
return this.indexes[field] !== undefined;
|
|
1003
|
+
}
|
|
1014
1004
|
addFilter(field, cb) {
|
|
1015
1005
|
if (!this.filters) {
|
|
1016
1006
|
this.filters = {};
|
|
@@ -1064,20 +1054,40 @@ class Context {
|
|
|
1064
1054
|
this.types[typeid] = schema;
|
|
1065
1055
|
this.schemas.set(schema, typeid);
|
|
1066
1056
|
}
|
|
1067
|
-
static create(
|
|
1057
|
+
static create(options = {}) {
|
|
1068
1058
|
return function (definition) {
|
|
1069
|
-
|
|
1059
|
+
if (!options.context) {
|
|
1060
|
+
options.context = new Context();
|
|
1061
|
+
}
|
|
1062
|
+
return type(definition, options);
|
|
1070
1063
|
};
|
|
1071
1064
|
}
|
|
1072
1065
|
}
|
|
1073
1066
|
const globalContext = new Context();
|
|
1074
1067
|
/**
|
|
1075
|
-
*
|
|
1068
|
+
* [See documentation](https://docs.colyseus.io/state/schema/)
|
|
1069
|
+
*
|
|
1070
|
+
* Annotate a Schema property to be serializeable.
|
|
1071
|
+
* \@type()'d fields are automatically flagged as "dirty" for the next patch.
|
|
1072
|
+
*
|
|
1073
|
+
* @example Standard usage, with automatic change tracking.
|
|
1074
|
+
* ```
|
|
1075
|
+
* \@type("string") propertyName: string;
|
|
1076
|
+
* ```
|
|
1077
|
+
*
|
|
1078
|
+
* @example You can provide the "manual" option if you'd like to manually control your patches via .setDirty().
|
|
1079
|
+
* ```
|
|
1080
|
+
* \@type("string", { manual: true })
|
|
1081
|
+
* ```
|
|
1076
1082
|
*/
|
|
1077
|
-
function type(type,
|
|
1083
|
+
function type(type, options = {}) {
|
|
1078
1084
|
return function (target, field) {
|
|
1085
|
+
const context = options.context || globalContext;
|
|
1079
1086
|
const constructor = target.constructor;
|
|
1080
1087
|
constructor._context = context;
|
|
1088
|
+
if (!type) {
|
|
1089
|
+
throw new Error(`${constructor.name}: @type() reference provided for "${field}" is undefined. Make sure you don't have any circular dependencies.`);
|
|
1090
|
+
}
|
|
1081
1091
|
/*
|
|
1082
1092
|
* static schema
|
|
1083
1093
|
*/
|
|
@@ -1090,7 +1100,21 @@ function type(type, context = globalContext) {
|
|
|
1090
1100
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
1091
1101
|
*/
|
|
1092
1102
|
if (definition.descriptors[field]) {
|
|
1093
|
-
|
|
1103
|
+
if (definition.deprecated[field]) {
|
|
1104
|
+
// do not create accessors for deprecated properties.
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
// trying to define same property multiple times across inheritance.
|
|
1109
|
+
// https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
|
|
1110
|
+
try {
|
|
1111
|
+
throw new Error(`@colyseus/schema: Duplicate '${field}' definition on '${constructor.name}'.\nCheck @type() annotation`);
|
|
1112
|
+
}
|
|
1113
|
+
catch (e) {
|
|
1114
|
+
const definitionAtLine = e.stack.split("\n")[4].trim();
|
|
1115
|
+
throw new Error(`${e.message} ${definitionAtLine}`);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1094
1118
|
}
|
|
1095
1119
|
const isArray = ArraySchema.is(type);
|
|
1096
1120
|
const isMap = !isArray && MapSchema.is(type);
|
|
@@ -1103,6 +1127,15 @@ function type(type, context = globalContext) {
|
|
|
1103
1127
|
context.add(childType);
|
|
1104
1128
|
}
|
|
1105
1129
|
}
|
|
1130
|
+
if (options.manual) {
|
|
1131
|
+
// do not declare getter/setter descriptor
|
|
1132
|
+
definition.descriptors[field] = {
|
|
1133
|
+
enumerable: true,
|
|
1134
|
+
configurable: true,
|
|
1135
|
+
writable: true,
|
|
1136
|
+
};
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1106
1139
|
const fieldCached = `_${field}`;
|
|
1107
1140
|
definition.descriptors[fieldCached] = {
|
|
1108
1141
|
enumerable: false,
|
|
@@ -1188,7 +1221,7 @@ function filterChildren(cb) {
|
|
|
1188
1221
|
* `@deprecated()` flag a field as deprecated.
|
|
1189
1222
|
* The previous `@type()` annotation should remain along with this one.
|
|
1190
1223
|
*/
|
|
1191
|
-
function deprecated(throws = true
|
|
1224
|
+
function deprecated(throws = true) {
|
|
1192
1225
|
return function (target, field) {
|
|
1193
1226
|
const constructor = target.constructor;
|
|
1194
1227
|
const definition = constructor._definition;
|
|
@@ -1203,9 +1236,12 @@ function deprecated(throws = true, context = globalContext) {
|
|
|
1203
1236
|
}
|
|
1204
1237
|
};
|
|
1205
1238
|
}
|
|
1206
|
-
function defineTypes(target, fields,
|
|
1239
|
+
function defineTypes(target, fields, options = {}) {
|
|
1240
|
+
if (!options.context) {
|
|
1241
|
+
options.context = target._context || options.context || globalContext;
|
|
1242
|
+
}
|
|
1207
1243
|
for (let field in fields) {
|
|
1208
|
-
type(fields[field],
|
|
1244
|
+
type(fields[field], options)(target.prototype, field);
|
|
1209
1245
|
}
|
|
1210
1246
|
return target;
|
|
1211
1247
|
}
|
|
@@ -1742,6 +1778,13 @@ class CollectionSchema {
|
|
|
1742
1778
|
initialValues.forEach((v) => this.add(v));
|
|
1743
1779
|
}
|
|
1744
1780
|
}
|
|
1781
|
+
onAdd(callback, triggerAll = true) {
|
|
1782
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.ADD, callback, (triggerAll)
|
|
1783
|
+
? this.$items
|
|
1784
|
+
: undefined);
|
|
1785
|
+
}
|
|
1786
|
+
onRemove(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
|
|
1787
|
+
onChange(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
|
|
1745
1788
|
static is(type) {
|
|
1746
1789
|
return type['collection'] !== undefined;
|
|
1747
1790
|
}
|
|
@@ -1785,17 +1828,19 @@ class CollectionSchema {
|
|
|
1785
1828
|
this.$indexes.delete(index);
|
|
1786
1829
|
return this.$items.delete(index);
|
|
1787
1830
|
}
|
|
1788
|
-
clear(
|
|
1831
|
+
clear(changes) {
|
|
1789
1832
|
// discard previous operations.
|
|
1790
1833
|
this.$changes.discard(true, true);
|
|
1791
1834
|
this.$changes.indexes = {};
|
|
1792
1835
|
// clear previous indexes
|
|
1793
1836
|
this.$indexes.clear();
|
|
1794
|
-
//
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1837
|
+
//
|
|
1838
|
+
// When decoding:
|
|
1839
|
+
// - enqueue items for DELETE callback.
|
|
1840
|
+
// - flag child items for garbage collection.
|
|
1841
|
+
//
|
|
1842
|
+
if (changes) {
|
|
1843
|
+
removeChildRefs.call(this, changes);
|
|
1799
1844
|
}
|
|
1800
1845
|
// clear items
|
|
1801
1846
|
this.$items.clear();
|
|
@@ -1864,9 +1909,6 @@ class CollectionSchema {
|
|
|
1864
1909
|
}
|
|
1865
1910
|
return cloned;
|
|
1866
1911
|
}
|
|
1867
|
-
triggerAll() {
|
|
1868
|
-
Schema.prototype.triggerAll.apply(this);
|
|
1869
|
-
}
|
|
1870
1912
|
}
|
|
1871
1913
|
|
|
1872
1914
|
class SetSchema {
|
|
@@ -1879,23 +1921,31 @@ class SetSchema {
|
|
|
1879
1921
|
initialValues.forEach((v) => this.add(v));
|
|
1880
1922
|
}
|
|
1881
1923
|
}
|
|
1924
|
+
onAdd(callback, triggerAll = true) {
|
|
1925
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.ADD, callback, (triggerAll)
|
|
1926
|
+
? this.$items
|
|
1927
|
+
: undefined);
|
|
1928
|
+
}
|
|
1929
|
+
onRemove(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
|
|
1930
|
+
onChange(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
|
|
1882
1931
|
static is(type) {
|
|
1883
1932
|
return type['set'] !== undefined;
|
|
1884
1933
|
}
|
|
1885
1934
|
add(value) {
|
|
1935
|
+
// immediatelly return false if value already added.
|
|
1886
1936
|
if (this.has(value)) {
|
|
1887
1937
|
return false;
|
|
1888
1938
|
}
|
|
1889
1939
|
// set "index" for reference.
|
|
1890
1940
|
const index = this.$refId++;
|
|
1891
|
-
|
|
1892
|
-
if (isRef) {
|
|
1941
|
+
if ((value['$changes']) !== undefined) {
|
|
1893
1942
|
value['$changes'].setParent(this, this.$changes.root, index);
|
|
1894
1943
|
}
|
|
1944
|
+
const operation = this.$changes.indexes[index]?.op ?? OPERATION.ADD;
|
|
1895
1945
|
this.$changes.indexes[index] = index;
|
|
1896
1946
|
this.$indexes.set(index, index);
|
|
1897
1947
|
this.$items.set(index, value);
|
|
1898
|
-
this.$changes.change(index);
|
|
1948
|
+
this.$changes.change(index, operation);
|
|
1899
1949
|
return index;
|
|
1900
1950
|
}
|
|
1901
1951
|
entries() {
|
|
@@ -1921,17 +1971,19 @@ class SetSchema {
|
|
|
1921
1971
|
this.$indexes.delete(index);
|
|
1922
1972
|
return this.$items.delete(index);
|
|
1923
1973
|
}
|
|
1924
|
-
clear(
|
|
1974
|
+
clear(changes) {
|
|
1925
1975
|
// discard previous operations.
|
|
1926
1976
|
this.$changes.discard(true, true);
|
|
1927
1977
|
this.$changes.indexes = {};
|
|
1928
1978
|
// clear previous indexes
|
|
1929
1979
|
this.$indexes.clear();
|
|
1930
|
-
//
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1980
|
+
//
|
|
1981
|
+
// When decoding:
|
|
1982
|
+
// - enqueue items for DELETE callback.
|
|
1983
|
+
// - flag child items for garbage collection.
|
|
1984
|
+
//
|
|
1985
|
+
if (changes) {
|
|
1986
|
+
removeChildRefs.call(this, changes);
|
|
1935
1987
|
}
|
|
1936
1988
|
// clear items
|
|
1937
1989
|
this.$items.clear();
|
|
@@ -2012,36 +2064,6 @@ class SetSchema {
|
|
|
2012
2064
|
}
|
|
2013
2065
|
return cloned;
|
|
2014
2066
|
}
|
|
2015
|
-
triggerAll() {
|
|
2016
|
-
Schema.prototype.triggerAll.apply(this);
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
/**
|
|
2021
|
-
* Extracted from https://www.npmjs.com/package/strong-events
|
|
2022
|
-
*/
|
|
2023
|
-
class EventEmitter {
|
|
2024
|
-
constructor() {
|
|
2025
|
-
this.handlers = [];
|
|
2026
|
-
}
|
|
2027
|
-
register(cb, once = false) {
|
|
2028
|
-
this.handlers.push(cb);
|
|
2029
|
-
return this;
|
|
2030
|
-
}
|
|
2031
|
-
invoke(...args) {
|
|
2032
|
-
this.handlers.forEach((handler) => handler(...args));
|
|
2033
|
-
}
|
|
2034
|
-
invokeAsync(...args) {
|
|
2035
|
-
return Promise.all(this.handlers.map((handler) => handler(...args)));
|
|
2036
|
-
}
|
|
2037
|
-
remove(cb) {
|
|
2038
|
-
const index = this.handlers.indexOf(cb);
|
|
2039
|
-
this.handlers[index] = this.handlers[this.handlers.length - 1];
|
|
2040
|
-
this.handlers.pop();
|
|
2041
|
-
}
|
|
2042
|
-
clear() {
|
|
2043
|
-
this.handlers = [];
|
|
2044
|
-
}
|
|
2045
2067
|
}
|
|
2046
2068
|
|
|
2047
2069
|
class ClientState {
|
|
@@ -2064,6 +2086,75 @@ class ClientState {
|
|
|
2064
2086
|
}
|
|
2065
2087
|
}
|
|
2066
2088
|
|
|
2089
|
+
class ReferenceTracker {
|
|
2090
|
+
constructor() {
|
|
2091
|
+
//
|
|
2092
|
+
// Relation of refId => Schema structure
|
|
2093
|
+
// For direct access of structures during decoding time.
|
|
2094
|
+
//
|
|
2095
|
+
this.refs = new Map();
|
|
2096
|
+
this.refCounts = {};
|
|
2097
|
+
this.deletedRefs = new Set();
|
|
2098
|
+
this.nextUniqueId = 0;
|
|
2099
|
+
}
|
|
2100
|
+
getNextUniqueId() {
|
|
2101
|
+
return this.nextUniqueId++;
|
|
2102
|
+
}
|
|
2103
|
+
// for decoding
|
|
2104
|
+
addRef(refId, ref, incrementCount = true) {
|
|
2105
|
+
this.refs.set(refId, ref);
|
|
2106
|
+
if (incrementCount) {
|
|
2107
|
+
this.refCounts[refId] = (this.refCounts[refId] || 0) + 1;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
// for decoding
|
|
2111
|
+
removeRef(refId) {
|
|
2112
|
+
this.refCounts[refId] = this.refCounts[refId] - 1;
|
|
2113
|
+
this.deletedRefs.add(refId);
|
|
2114
|
+
}
|
|
2115
|
+
clearRefs() {
|
|
2116
|
+
this.refs.clear();
|
|
2117
|
+
this.deletedRefs.clear();
|
|
2118
|
+
this.refCounts = {};
|
|
2119
|
+
}
|
|
2120
|
+
// for decoding
|
|
2121
|
+
garbageCollectDeletedRefs() {
|
|
2122
|
+
this.deletedRefs.forEach((refId) => {
|
|
2123
|
+
//
|
|
2124
|
+
// Skip active references.
|
|
2125
|
+
//
|
|
2126
|
+
if (this.refCounts[refId] > 0) {
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
const ref = this.refs.get(refId);
|
|
2130
|
+
//
|
|
2131
|
+
// Ensure child schema instances have their references removed as well.
|
|
2132
|
+
//
|
|
2133
|
+
if (ref instanceof Schema) {
|
|
2134
|
+
for (const fieldName in ref['_definition'].schema) {
|
|
2135
|
+
if (typeof (ref['_definition'].schema[fieldName]) !== "string" &&
|
|
2136
|
+
ref[fieldName] &&
|
|
2137
|
+
ref[fieldName]['$changes']) {
|
|
2138
|
+
this.removeRef(ref[fieldName]['$changes'].refId);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
else {
|
|
2143
|
+
const definition = ref['$changes'].parent._definition;
|
|
2144
|
+
const type = definition.schema[definition.fieldsByIndex[ref['$changes'].parentIndex]];
|
|
2145
|
+
if (typeof (Object.values(type)[0]) === "function") {
|
|
2146
|
+
Array.from(ref.values())
|
|
2147
|
+
.forEach((child) => this.removeRef(child['$changes'].refId));
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
this.refs.delete(refId);
|
|
2151
|
+
delete this.refCounts[refId];
|
|
2152
|
+
});
|
|
2153
|
+
// clear deleted refs.
|
|
2154
|
+
this.deletedRefs.clear();
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2067
2158
|
class EncodeSchemaError extends Error {
|
|
2068
2159
|
}
|
|
2069
2160
|
function assertType(value, type, klass, field) {
|
|
@@ -2126,12 +2217,17 @@ class Schema {
|
|
|
2126
2217
|
// fix enumerability of fields for end-user
|
|
2127
2218
|
Object.defineProperties(this, {
|
|
2128
2219
|
$changes: {
|
|
2129
|
-
value: new ChangeTree(this, undefined, new
|
|
2220
|
+
value: new ChangeTree(this, undefined, new ReferenceTracker()),
|
|
2130
2221
|
enumerable: false,
|
|
2131
2222
|
writable: true
|
|
2132
2223
|
},
|
|
2133
|
-
$listeners: {
|
|
2134
|
-
|
|
2224
|
+
// $listeners: {
|
|
2225
|
+
// value: undefined,
|
|
2226
|
+
// enumerable: false,
|
|
2227
|
+
// writable: true
|
|
2228
|
+
// },
|
|
2229
|
+
$callbacks: {
|
|
2230
|
+
value: undefined,
|
|
2135
2231
|
enumerable: false,
|
|
2136
2232
|
writable: true
|
|
2137
2233
|
},
|
|
@@ -2154,26 +2250,43 @@ class Schema {
|
|
|
2154
2250
|
return (type['_definition'] &&
|
|
2155
2251
|
type['_definition'].schema !== undefined);
|
|
2156
2252
|
}
|
|
2253
|
+
onChange(callback) {
|
|
2254
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.REPLACE, callback);
|
|
2255
|
+
}
|
|
2256
|
+
onRemove(callback) {
|
|
2257
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.DELETE, callback);
|
|
2258
|
+
}
|
|
2157
2259
|
assign(props) {
|
|
2158
2260
|
Object.assign(this, props);
|
|
2159
2261
|
return this;
|
|
2160
2262
|
}
|
|
2161
2263
|
get _definition() { return this.constructor._definition; }
|
|
2264
|
+
/**
|
|
2265
|
+
* (Server-side): Flag a property to be encoded for the next patch.
|
|
2266
|
+
* @param instance Schema instance
|
|
2267
|
+
* @param property string representing the property name, or number representing the index of the property.
|
|
2268
|
+
* @param operation OPERATION to perform (detected automatically)
|
|
2269
|
+
*/
|
|
2270
|
+
setDirty(property, operation) {
|
|
2271
|
+
this.$changes.change(property, operation);
|
|
2272
|
+
}
|
|
2162
2273
|
listen(attr, callback) {
|
|
2163
|
-
if (!this.$
|
|
2164
|
-
this.$
|
|
2274
|
+
if (!this.$callbacks) {
|
|
2275
|
+
this.$callbacks = {};
|
|
2276
|
+
}
|
|
2277
|
+
if (!this.$callbacks[attr]) {
|
|
2278
|
+
this.$callbacks[attr] = [];
|
|
2165
2279
|
}
|
|
2166
|
-
this.$
|
|
2280
|
+
this.$callbacks[attr].push(callback);
|
|
2167
2281
|
// return un-register callback.
|
|
2168
|
-
return () => this.$
|
|
2282
|
+
return () => spliceOne(this.$callbacks[attr], this.$callbacks[attr].indexOf(callback));
|
|
2169
2283
|
}
|
|
2170
|
-
decode(bytes, it = { offset: 0 }, ref = this
|
|
2284
|
+
decode(bytes, it = { offset: 0 }, ref = this) {
|
|
2285
|
+
const allChanges = [];
|
|
2171
2286
|
const $root = this.$changes.root;
|
|
2172
2287
|
const totalBytes = bytes.length;
|
|
2173
2288
|
let refId = 0;
|
|
2174
|
-
let changes = [];
|
|
2175
2289
|
$root.refs.set(refId, this);
|
|
2176
|
-
allChanges.set(refId, changes);
|
|
2177
2290
|
while (it.offset < totalBytes) {
|
|
2178
2291
|
let byte = bytes[it.offset++];
|
|
2179
2292
|
if (byte == SWITCH_TO_STRUCTURE) {
|
|
@@ -2186,9 +2299,6 @@ class Schema {
|
|
|
2186
2299
|
throw new Error(`"refId" not found: ${refId}`);
|
|
2187
2300
|
}
|
|
2188
2301
|
ref = nextRef;
|
|
2189
|
-
// create empty list of changes for this refId.
|
|
2190
|
-
changes = [];
|
|
2191
|
-
allChanges.set(refId, changes);
|
|
2192
2302
|
continue;
|
|
2193
2303
|
}
|
|
2194
2304
|
const changeTree = ref['$changes'];
|
|
@@ -2202,7 +2312,7 @@ class Schema {
|
|
|
2202
2312
|
// The `.clear()` method is calling `$root.removeRef(refId)` for
|
|
2203
2313
|
// each item inside this collection
|
|
2204
2314
|
//
|
|
2205
|
-
ref.clear(
|
|
2315
|
+
ref.clear(allChanges);
|
|
2206
2316
|
continue;
|
|
2207
2317
|
}
|
|
2208
2318
|
const fieldIndex = (isSchema)
|
|
@@ -2272,9 +2382,8 @@ class Schema {
|
|
|
2272
2382
|
value = this.createTypeInstance(childType);
|
|
2273
2383
|
value.$changes.refId = refId;
|
|
2274
2384
|
if (previousValue) {
|
|
2275
|
-
value
|
|
2276
|
-
value
|
|
2277
|
-
value.$listeners = previousValue.$listeners;
|
|
2385
|
+
value.$callbacks = previousValue.$callbacks;
|
|
2386
|
+
// value.$listeners = previousValue.$listeners;
|
|
2278
2387
|
if (previousValue['$changes'].refId &&
|
|
2279
2388
|
refId !== previousValue['$changes'].refId) {
|
|
2280
2389
|
$root.removeRef(previousValue['$changes'].refId);
|
|
@@ -2300,40 +2409,29 @@ class Schema {
|
|
|
2300
2409
|
value.$changes.refId = refId;
|
|
2301
2410
|
// preserve schema callbacks
|
|
2302
2411
|
if (previousValue) {
|
|
2303
|
-
value
|
|
2304
|
-
value.onRemove = previousValue.onRemove;
|
|
2305
|
-
value.onChange = previousValue.onChange;
|
|
2412
|
+
value['$callbacks'] = previousValue['$callbacks'];
|
|
2306
2413
|
if (previousValue['$changes'].refId &&
|
|
2307
2414
|
refId !== previousValue['$changes'].refId) {
|
|
2308
2415
|
$root.removeRef(previousValue['$changes'].refId);
|
|
2309
2416
|
//
|
|
2310
2417
|
// Trigger onRemove if structure has been replaced.
|
|
2311
2418
|
//
|
|
2312
|
-
const deletes = [];
|
|
2313
2419
|
const entries = previousValue.entries();
|
|
2314
2420
|
let iter;
|
|
2315
2421
|
while ((iter = entries.next()) && !iter.done) {
|
|
2316
2422
|
const [key, value] = iter.value;
|
|
2317
|
-
|
|
2423
|
+
allChanges.push({
|
|
2424
|
+
refId,
|
|
2318
2425
|
op: OPERATION.DELETE,
|
|
2319
2426
|
field: key,
|
|
2320
2427
|
value: undefined,
|
|
2321
2428
|
previousValue: value,
|
|
2322
2429
|
});
|
|
2323
2430
|
}
|
|
2324
|
-
allChanges.set(previousValue['$changes'].refId, deletes);
|
|
2325
2431
|
}
|
|
2326
2432
|
}
|
|
2327
2433
|
$root.addRef(refId, value, (valueRef !== previousValue));
|
|
2328
|
-
//
|
|
2329
|
-
// TODO: deprecate proxies on next version.
|
|
2330
|
-
// get proxy to target value.
|
|
2331
|
-
//
|
|
2332
|
-
if (typeDef.getProxy) {
|
|
2333
|
-
value = typeDef.getProxy(value);
|
|
2334
|
-
}
|
|
2335
2434
|
}
|
|
2336
|
-
let hasChange = (previousValue !== value);
|
|
2337
2435
|
if (value !== null &&
|
|
2338
2436
|
value !== undefined) {
|
|
2339
2437
|
if (value['$changes']) {
|
|
@@ -2341,14 +2439,7 @@ class Schema {
|
|
|
2341
2439
|
}
|
|
2342
2440
|
if (ref instanceof Schema) {
|
|
2343
2441
|
ref[fieldName] = value;
|
|
2344
|
-
//
|
|
2345
|
-
// FIXME: use `_field` instead of `field`.
|
|
2346
|
-
//
|
|
2347
|
-
// `field` is going to use the setter of the PropertyDescriptor
|
|
2348
|
-
// and create a proxy for array/map. This is only useful for
|
|
2349
|
-
// backwards-compatibility with @colyseus/schema@0.5.x
|
|
2350
|
-
//
|
|
2351
|
-
// // ref[_field] = value;
|
|
2442
|
+
// ref[`_${fieldName}`] = value;
|
|
2352
2443
|
}
|
|
2353
2444
|
else if (ref instanceof MapSchema) {
|
|
2354
2445
|
// const key = ref['$indexes'].get(field);
|
|
@@ -2362,19 +2453,20 @@ class Schema {
|
|
|
2362
2453
|
// ref[key] = value;
|
|
2363
2454
|
ref.setAt(fieldIndex, value);
|
|
2364
2455
|
}
|
|
2365
|
-
else if (ref instanceof CollectionSchema
|
|
2366
|
-
ref instanceof SetSchema) {
|
|
2456
|
+
else if (ref instanceof CollectionSchema) {
|
|
2367
2457
|
const index = ref.add(value);
|
|
2368
2458
|
ref['setIndex'](fieldIndex, index);
|
|
2369
2459
|
}
|
|
2460
|
+
else if (ref instanceof SetSchema) {
|
|
2461
|
+
const index = ref.add(value);
|
|
2462
|
+
if (index !== false) {
|
|
2463
|
+
ref['setIndex'](fieldIndex, index);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2370
2466
|
}
|
|
2371
|
-
if (
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
// this.onChange || ref.$listeners[field]
|
|
2375
|
-
// )
|
|
2376
|
-
) {
|
|
2377
|
-
changes.push({
|
|
2467
|
+
if (previousValue !== value) {
|
|
2468
|
+
allChanges.push({
|
|
2469
|
+
refId,
|
|
2378
2470
|
op: operation,
|
|
2379
2471
|
field: fieldName,
|
|
2380
2472
|
dynamicIndex,
|
|
@@ -2643,7 +2735,7 @@ class Schema {
|
|
|
2643
2735
|
//
|
|
2644
2736
|
// use cached bytes directly if is from Schema type.
|
|
2645
2737
|
//
|
|
2646
|
-
filteredBytes
|
|
2738
|
+
filteredBytes.push.apply(filteredBytes, changeTree.caches[fieldIndex] ?? []);
|
|
2647
2739
|
containerIndexes.add(fieldIndex);
|
|
2648
2740
|
}
|
|
2649
2741
|
else {
|
|
@@ -2651,7 +2743,7 @@ class Schema {
|
|
|
2651
2743
|
//
|
|
2652
2744
|
// use cached bytes if already has the field
|
|
2653
2745
|
//
|
|
2654
|
-
filteredBytes
|
|
2746
|
+
filteredBytes.push.apply(filteredBytes, changeTree.caches[fieldIndex] ?? []);
|
|
2655
2747
|
}
|
|
2656
2748
|
else {
|
|
2657
2749
|
//
|
|
@@ -2716,20 +2808,6 @@ class Schema {
|
|
|
2716
2808
|
}
|
|
2717
2809
|
return cloned;
|
|
2718
2810
|
}
|
|
2719
|
-
triggerAll() {
|
|
2720
|
-
// skip if haven't received any remote refs yet.
|
|
2721
|
-
if (this.$changes.root.refs.size === 0) {
|
|
2722
|
-
return;
|
|
2723
|
-
}
|
|
2724
|
-
const allChanges = new Map();
|
|
2725
|
-
Schema.prototype._triggerAllFillChanges.call(this, this, allChanges);
|
|
2726
|
-
try {
|
|
2727
|
-
Schema.prototype._triggerChanges.call(this, allChanges);
|
|
2728
|
-
}
|
|
2729
|
-
catch (e) {
|
|
2730
|
-
Schema.onError(e);
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
2811
|
toJSON() {
|
|
2734
2812
|
const schema = this._definition.schema;
|
|
2735
2813
|
const deprecated = this._definition.deprecated;
|
|
@@ -2772,109 +2850,73 @@ class Schema {
|
|
|
2772
2850
|
instance.$changes.root = this.$changes.root;
|
|
2773
2851
|
return instance;
|
|
2774
2852
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
const
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2853
|
+
_triggerChanges(changes) {
|
|
2854
|
+
const uniqueRefIds = new Set();
|
|
2855
|
+
const $refs = this.$changes.root.refs;
|
|
2856
|
+
for (let i = 0; i < changes.length; i++) {
|
|
2857
|
+
const change = changes[i];
|
|
2858
|
+
const refId = change.refId;
|
|
2859
|
+
const ref = $refs.get(refId);
|
|
2860
|
+
const $callbacks = ref['$callbacks'];
|
|
2861
|
+
//
|
|
2862
|
+
// trigger onRemove on child structure.
|
|
2863
|
+
//
|
|
2864
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
|
|
2865
|
+
change.previousValue instanceof Schema) {
|
|
2866
|
+
change.previousValue['$callbacks']?.[OPERATION.DELETE]?.forEach(callback => callback());
|
|
2867
|
+
}
|
|
2868
|
+
// no callbacks defined, skip this structure!
|
|
2869
|
+
if (!$callbacks) {
|
|
2870
|
+
continue;
|
|
2871
|
+
}
|
|
2872
|
+
if (ref instanceof Schema) {
|
|
2873
|
+
if (!uniqueRefIds.has(refId)) {
|
|
2874
|
+
try {
|
|
2875
|
+
// trigger onChange
|
|
2876
|
+
$callbacks?.[OPERATION.REPLACE]?.forEach(callback => callback(changes));
|
|
2877
|
+
}
|
|
2878
|
+
catch (e) {
|
|
2879
|
+
Schema.onError(e);
|
|
2795
2880
|
}
|
|
2796
2881
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
while ((iter = entries.next()) && !iter.done) {
|
|
2803
|
-
const [key, value] = iter.value;
|
|
2804
|
-
changes.push({
|
|
2805
|
-
op: OPERATION.ADD,
|
|
2806
|
-
field: key,
|
|
2807
|
-
dynamicIndex: key,
|
|
2808
|
-
value: value,
|
|
2809
|
-
previousValue: undefined,
|
|
2810
|
-
});
|
|
2811
|
-
if (value['$changes'] !== undefined) {
|
|
2812
|
-
Schema.prototype._triggerAllFillChanges.call(this, value, allChanges);
|
|
2882
|
+
try {
|
|
2883
|
+
$callbacks[change.op]?.forEach(callback => callback(change.value, change.previousValue));
|
|
2884
|
+
}
|
|
2885
|
+
catch (e) {
|
|
2886
|
+
Schema.onError(e);
|
|
2813
2887
|
}
|
|
2814
2888
|
}
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
for (let i = 0; i < changes.length; i++) {
|
|
2823
|
-
const change = changes[i];
|
|
2824
|
-
const listener = ref['$listeners'] && ref['$listeners'][change.field];
|
|
2825
|
-
if (!isSchema) {
|
|
2826
|
-
if (change.op === OPERATION.ADD && change.previousValue === undefined) {
|
|
2827
|
-
ref.onAdd?.(change.value, change.dynamicIndex ?? change.field);
|
|
2828
|
-
}
|
|
2829
|
-
else if (change.op === OPERATION.DELETE) {
|
|
2830
|
-
//
|
|
2831
|
-
// FIXME: `previousValue` should always be avaiiable.
|
|
2832
|
-
// ADD + DELETE operations are still encoding DELETE operation.
|
|
2833
|
-
//
|
|
2834
|
-
if (change.previousValue !== undefined) {
|
|
2835
|
-
ref.onRemove?.(change.previousValue, change.dynamicIndex ?? change.field);
|
|
2836
|
-
}
|
|
2837
|
-
}
|
|
2838
|
-
else if (change.op === OPERATION.DELETE_AND_ADD) {
|
|
2839
|
-
if (change.previousValue !== undefined) {
|
|
2840
|
-
ref.onRemove?.(change.previousValue, change.dynamicIndex);
|
|
2841
|
-
}
|
|
2842
|
-
ref.onAdd?.(change.value, change.dynamicIndex);
|
|
2843
|
-
}
|
|
2844
|
-
else if (change.op === OPERATION.REPLACE ||
|
|
2845
|
-
change.value !== change.previousValue) {
|
|
2846
|
-
ref.onChange?.(change.value, change.dynamicIndex);
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2889
|
+
else {
|
|
2890
|
+
// is a collection of items
|
|
2891
|
+
if (change.op === OPERATION.ADD && change.previousValue === undefined) {
|
|
2892
|
+
// triger onAdd
|
|
2893
|
+
$callbacks[OPERATION.ADD]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
|
|
2894
|
+
}
|
|
2895
|
+
else if (change.op === OPERATION.DELETE) {
|
|
2849
2896
|
//
|
|
2850
|
-
//
|
|
2897
|
+
// FIXME: `previousValue` should always be available.
|
|
2898
|
+
// ADD + DELETE operations are still encoding DELETE operation.
|
|
2851
2899
|
//
|
|
2852
|
-
if (
|
|
2853
|
-
|
|
2854
|
-
change.previousValue.
|
|
2855
|
-
change.previousValue.onRemove();
|
|
2856
|
-
}
|
|
2857
|
-
if (listener) {
|
|
2858
|
-
try {
|
|
2859
|
-
listener.invoke(change.value, change.previousValue);
|
|
2860
|
-
}
|
|
2861
|
-
catch (e) {
|
|
2862
|
-
Schema.onError(e);
|
|
2863
|
-
}
|
|
2900
|
+
if (change.previousValue !== undefined) {
|
|
2901
|
+
// triger onRemove
|
|
2902
|
+
$callbacks[OPERATION.DELETE]?.forEach(callback => callback(change.previousValue, change.dynamicIndex ?? change.field));
|
|
2864
2903
|
}
|
|
2865
2904
|
}
|
|
2866
|
-
if (
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
}
|
|
2871
|
-
catch (e) {
|
|
2872
|
-
Schema.onError(e);
|
|
2873
|
-
}
|
|
2905
|
+
else if (change.op === OPERATION.DELETE_AND_ADD) {
|
|
2906
|
+
// triger onRemove
|
|
2907
|
+
if (change.previousValue !== undefined) {
|
|
2908
|
+
$callbacks[OPERATION.DELETE]?.forEach(callback => callback(change.previousValue, change.dynamicIndex ?? change.field));
|
|
2874
2909
|
}
|
|
2910
|
+
// triger onAdd
|
|
2911
|
+
$callbacks[OPERATION.ADD]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
|
|
2912
|
+
}
|
|
2913
|
+
// trigger onChange
|
|
2914
|
+
if (change.value !== change.previousValue) {
|
|
2915
|
+
$callbacks[OPERATION.REPLACE]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
|
|
2875
2916
|
}
|
|
2876
2917
|
}
|
|
2877
|
-
|
|
2918
|
+
uniqueRefIds.add(refId);
|
|
2919
|
+
}
|
|
2878
2920
|
}
|
|
2879
2921
|
}
|
|
2880
2922
|
Schema._definition = SchemaDefinition.create();
|
|
@@ -2920,7 +2962,7 @@ function __decorate(decorators, target, key, desc) {
|
|
|
2920
2962
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2921
2963
|
}
|
|
2922
2964
|
|
|
2923
|
-
const reflectionContext = new Context();
|
|
2965
|
+
const reflectionContext = { context: new Context() };
|
|
2924
2966
|
/**
|
|
2925
2967
|
* Reflection
|
|
2926
2968
|
*/
|
|
@@ -3025,14 +3067,14 @@ class Reflection extends Schema {
|
|
|
3025
3067
|
refType = typeInfo[1];
|
|
3026
3068
|
}
|
|
3027
3069
|
if (fieldType === "ref") {
|
|
3028
|
-
type(refType, context)(schemaType.prototype, field.name);
|
|
3070
|
+
type(refType, { context })(schemaType.prototype, field.name);
|
|
3029
3071
|
}
|
|
3030
3072
|
else {
|
|
3031
|
-
type({ [fieldType]: refType }, context)(schemaType.prototype, field.name);
|
|
3073
|
+
type({ [fieldType]: refType }, { context })(schemaType.prototype, field.name);
|
|
3032
3074
|
}
|
|
3033
3075
|
}
|
|
3034
3076
|
else {
|
|
3035
|
-
type(field.type, context)(schemaType.prototype, field.name);
|
|
3077
|
+
type(field.type, { context })(schemaType.prototype, field.name);
|
|
3036
3078
|
}
|
|
3037
3079
|
});
|
|
3038
3080
|
});
|
|
@@ -3060,8 +3102,8 @@ __decorate([
|
|
|
3060
3102
|
type("number", reflectionContext)
|
|
3061
3103
|
], Reflection.prototype, "rootType", void 0);
|
|
3062
3104
|
|
|
3063
|
-
registerType("map", { constructor: MapSchema
|
|
3064
|
-
registerType("array", { constructor: ArraySchema
|
|
3105
|
+
registerType("map", { constructor: MapSchema });
|
|
3106
|
+
registerType("array", { constructor: ArraySchema });
|
|
3065
3107
|
registerType("set", { constructor: SetSchema });
|
|
3066
3108
|
registerType("collection", { constructor: CollectionSchema, });
|
|
3067
3109
|
|