@colyseus/schema 1.1.0-alpha.0 → 2.0.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.
- package/README.md +1 -4
- package/build/cjs/index.js +1194 -1001
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +350 -311
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +371 -335
- package/lib/Reflection.d.ts +21 -0
- package/lib/Reflection.js +198 -0
- package/lib/Reflection.js.map +1 -0
- package/lib/Schema.d.ts +66 -0
- package/lib/Schema.js +853 -0
- package/lib/Schema.js.map +1 -0
- package/lib/annotations.d.ts +96 -0
- package/lib/annotations.js +316 -0
- package/lib/annotations.js.map +1 -0
- package/lib/changes/ChangeTree.d.ts +53 -0
- package/lib/changes/ChangeTree.js +209 -0
- package/lib/changes/ChangeTree.js.map +1 -0
- 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/api.d.ts +7 -0
- package/lib/codegen/api.js +36 -0
- package/lib/codegen/api.js.map +1 -0
- package/lib/codegen/argv.d.ts +6 -0
- package/lib/codegen/argv.js +41 -0
- package/lib/codegen/argv.js.map +1 -0
- package/lib/codegen/cli.d.ts +1 -0
- package/lib/codegen/cli.js +49 -0
- package/lib/codegen/cli.js.map +1 -0
- package/lib/codegen/languages/cpp.d.ts +3 -0
- package/lib/codegen/languages/cpp.js +213 -0
- package/lib/codegen/languages/cpp.js.map +1 -0
- package/lib/codegen/languages/csharp.d.ts +4 -0
- package/lib/codegen/languages/csharp.js +130 -0
- package/lib/codegen/languages/csharp.js.map +1 -0
- package/lib/codegen/languages/haxe.d.ts +3 -0
- package/lib/codegen/languages/haxe.js +93 -0
- package/lib/codegen/languages/haxe.js.map +1 -0
- package/lib/codegen/languages/java.d.ts +6 -0
- package/lib/codegen/languages/java.js +92 -0
- package/lib/codegen/languages/java.js.map +1 -0
- package/lib/codegen/languages/js.d.ts +3 -0
- package/lib/codegen/languages/js.js +89 -0
- package/lib/codegen/languages/js.js.map +1 -0
- package/lib/codegen/languages/lua.d.ts +3 -0
- package/lib/codegen/languages/lua.js +97 -0
- package/lib/codegen/languages/lua.js.map +1 -0
- package/lib/codegen/languages/ts.d.ts +3 -0
- package/lib/codegen/languages/ts.js +125 -0
- package/lib/codegen/languages/ts.js.map +1 -0
- package/lib/codegen/parser.d.ts +5 -0
- package/lib/codegen/parser.js +196 -0
- package/lib/codegen/parser.js.map +1 -0
- package/lib/codegen/types.d.ts +44 -0
- package/lib/codegen/types.js +122 -0
- package/lib/codegen/types.js.map +1 -0
- package/lib/encoding/decode.d.ts +48 -0
- package/lib/encoding/decode.js +267 -0
- package/lib/encoding/decode.js.map +1 -0
- package/lib/encoding/encode.d.ts +38 -0
- package/lib/encoding/encode.js +281 -0
- package/lib/encoding/encode.js.map +1 -0
- package/lib/events/EventEmitter.d.ts +13 -0
- package/lib/events/EventEmitter.js +62 -0
- package/lib/events/EventEmitter.js.map +1 -0
- package/lib/filters/index.d.ts +8 -0
- package/lib/filters/index.js +25 -0
- package/lib/filters/index.js.map +1 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.js +46 -0
- package/lib/index.js.map +1 -0
- package/lib/spec.d.ts +13 -0
- package/lib/spec.js +42 -0
- package/lib/spec.js.map +1 -0
- package/lib/types/ArraySchema.d.ts +230 -0
- package/lib/types/ArraySchema.js +559 -0
- package/lib/types/ArraySchema.js.map +1 -0
- package/lib/types/CollectionSchema.d.ts +35 -0
- package/lib/types/CollectionSchema.js +158 -0
- package/lib/types/CollectionSchema.js.map +1 -0
- package/lib/types/HelperTypes.d.ts +28 -0
- package/lib/types/HelperTypes.js +3 -0
- package/lib/types/HelperTypes.js.map +1 -0
- package/lib/types/MapSchema.d.ts +37 -0
- package/lib/types/MapSchema.js +214 -0
- package/lib/types/MapSchema.js.map +1 -0
- package/lib/types/SetSchema.d.ts +32 -0
- package/lib/types/SetSchema.js +171 -0
- package/lib/types/SetSchema.js.map +1 -0
- package/lib/types/index.d.ts +6 -0
- package/lib/types/index.js +13 -0
- package/lib/types/index.js.map +1 -0
- 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/lib/utils.d.ts +2 -0
- package/lib/utils.js +26 -0
- package/lib/utils.js.map +1 -0
- package/package.json +4 -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,18 +1054,35 @@ 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;
|
|
1081
1088
|
/*
|
|
@@ -1090,7 +1097,21 @@ function type(type, context = globalContext) {
|
|
|
1090
1097
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
1091
1098
|
*/
|
|
1092
1099
|
if (definition.descriptors[field]) {
|
|
1093
|
-
|
|
1100
|
+
if (definition.deprecated[field]) {
|
|
1101
|
+
// do not create accessors for deprecated properties.
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
else {
|
|
1105
|
+
// trying to define same property multiple times across inheritance.
|
|
1106
|
+
// https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
|
|
1107
|
+
try {
|
|
1108
|
+
throw new Error(`@colyseus/schema: Duplicate '${field}' definition on '${constructor.name}'.\nCheck @type() annotation`);
|
|
1109
|
+
}
|
|
1110
|
+
catch (e) {
|
|
1111
|
+
const definitionAtLine = e.stack.split("\n")[4].trim();
|
|
1112
|
+
throw new Error(`${e.message} ${definitionAtLine}`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1094
1115
|
}
|
|
1095
1116
|
const isArray = ArraySchema.is(type);
|
|
1096
1117
|
const isMap = !isArray && MapSchema.is(type);
|
|
@@ -1103,6 +1124,15 @@ function type(type, context = globalContext) {
|
|
|
1103
1124
|
context.add(childType);
|
|
1104
1125
|
}
|
|
1105
1126
|
}
|
|
1127
|
+
if (options.manual) {
|
|
1128
|
+
// do not declare getter/setter descriptor
|
|
1129
|
+
definition.descriptors[field] = {
|
|
1130
|
+
enumerable: true,
|
|
1131
|
+
configurable: true,
|
|
1132
|
+
writable: true,
|
|
1133
|
+
};
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1106
1136
|
const fieldCached = `_${field}`;
|
|
1107
1137
|
definition.descriptors[fieldCached] = {
|
|
1108
1138
|
enumerable: false,
|
|
@@ -1188,7 +1218,7 @@ function filterChildren(cb) {
|
|
|
1188
1218
|
* `@deprecated()` flag a field as deprecated.
|
|
1189
1219
|
* The previous `@type()` annotation should remain along with this one.
|
|
1190
1220
|
*/
|
|
1191
|
-
function deprecated(throws = true
|
|
1221
|
+
function deprecated(throws = true) {
|
|
1192
1222
|
return function (target, field) {
|
|
1193
1223
|
const constructor = target.constructor;
|
|
1194
1224
|
const definition = constructor._definition;
|
|
@@ -1203,9 +1233,12 @@ function deprecated(throws = true, context = globalContext) {
|
|
|
1203
1233
|
}
|
|
1204
1234
|
};
|
|
1205
1235
|
}
|
|
1206
|
-
function defineTypes(target, fields,
|
|
1236
|
+
function defineTypes(target, fields, options = {}) {
|
|
1237
|
+
if (!options.context) {
|
|
1238
|
+
options.context = target._context || options.context || globalContext;
|
|
1239
|
+
}
|
|
1207
1240
|
for (let field in fields) {
|
|
1208
|
-
type(fields[field],
|
|
1241
|
+
type(fields[field], options)(target.prototype, field);
|
|
1209
1242
|
}
|
|
1210
1243
|
return target;
|
|
1211
1244
|
}
|
|
@@ -1742,6 +1775,13 @@ class CollectionSchema {
|
|
|
1742
1775
|
initialValues.forEach((v) => this.add(v));
|
|
1743
1776
|
}
|
|
1744
1777
|
}
|
|
1778
|
+
onAdd(callback, triggerAll = true) {
|
|
1779
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.ADD, callback, (triggerAll)
|
|
1780
|
+
? this.$items
|
|
1781
|
+
: undefined);
|
|
1782
|
+
}
|
|
1783
|
+
onRemove(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
|
|
1784
|
+
onChange(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
|
|
1745
1785
|
static is(type) {
|
|
1746
1786
|
return type['collection'] !== undefined;
|
|
1747
1787
|
}
|
|
@@ -1785,17 +1825,19 @@ class CollectionSchema {
|
|
|
1785
1825
|
this.$indexes.delete(index);
|
|
1786
1826
|
return this.$items.delete(index);
|
|
1787
1827
|
}
|
|
1788
|
-
clear(
|
|
1828
|
+
clear(changes) {
|
|
1789
1829
|
// discard previous operations.
|
|
1790
1830
|
this.$changes.discard(true, true);
|
|
1791
1831
|
this.$changes.indexes = {};
|
|
1792
1832
|
// clear previous indexes
|
|
1793
1833
|
this.$indexes.clear();
|
|
1794
|
-
//
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1834
|
+
//
|
|
1835
|
+
// When decoding:
|
|
1836
|
+
// - enqueue items for DELETE callback.
|
|
1837
|
+
// - flag child items for garbage collection.
|
|
1838
|
+
//
|
|
1839
|
+
if (changes) {
|
|
1840
|
+
removeChildRefs.call(this, changes);
|
|
1799
1841
|
}
|
|
1800
1842
|
// clear items
|
|
1801
1843
|
this.$items.clear();
|
|
@@ -1864,9 +1906,6 @@ class CollectionSchema {
|
|
|
1864
1906
|
}
|
|
1865
1907
|
return cloned;
|
|
1866
1908
|
}
|
|
1867
|
-
triggerAll() {
|
|
1868
|
-
Schema.prototype.triggerAll.apply(this);
|
|
1869
|
-
}
|
|
1870
1909
|
}
|
|
1871
1910
|
|
|
1872
1911
|
class SetSchema {
|
|
@@ -1879,23 +1918,31 @@ class SetSchema {
|
|
|
1879
1918
|
initialValues.forEach((v) => this.add(v));
|
|
1880
1919
|
}
|
|
1881
1920
|
}
|
|
1921
|
+
onAdd(callback, triggerAll = true) {
|
|
1922
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.ADD, callback, (triggerAll)
|
|
1923
|
+
? this.$items
|
|
1924
|
+
: undefined);
|
|
1925
|
+
}
|
|
1926
|
+
onRemove(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.DELETE, callback); }
|
|
1927
|
+
onChange(callback) { return addCallback(this.$callbacks || (this.$callbacks = []), OPERATION.REPLACE, callback); }
|
|
1882
1928
|
static is(type) {
|
|
1883
1929
|
return type['set'] !== undefined;
|
|
1884
1930
|
}
|
|
1885
1931
|
add(value) {
|
|
1932
|
+
// immediatelly return false if value already added.
|
|
1886
1933
|
if (this.has(value)) {
|
|
1887
1934
|
return false;
|
|
1888
1935
|
}
|
|
1889
1936
|
// set "index" for reference.
|
|
1890
1937
|
const index = this.$refId++;
|
|
1891
|
-
|
|
1892
|
-
if (isRef) {
|
|
1938
|
+
if ((value['$changes']) !== undefined) {
|
|
1893
1939
|
value['$changes'].setParent(this, this.$changes.root, index);
|
|
1894
1940
|
}
|
|
1941
|
+
const operation = this.$changes.indexes[index]?.op ?? OPERATION.ADD;
|
|
1895
1942
|
this.$changes.indexes[index] = index;
|
|
1896
1943
|
this.$indexes.set(index, index);
|
|
1897
1944
|
this.$items.set(index, value);
|
|
1898
|
-
this.$changes.change(index);
|
|
1945
|
+
this.$changes.change(index, operation);
|
|
1899
1946
|
return index;
|
|
1900
1947
|
}
|
|
1901
1948
|
entries() {
|
|
@@ -1921,17 +1968,19 @@ class SetSchema {
|
|
|
1921
1968
|
this.$indexes.delete(index);
|
|
1922
1969
|
return this.$items.delete(index);
|
|
1923
1970
|
}
|
|
1924
|
-
clear(
|
|
1971
|
+
clear(changes) {
|
|
1925
1972
|
// discard previous operations.
|
|
1926
1973
|
this.$changes.discard(true, true);
|
|
1927
1974
|
this.$changes.indexes = {};
|
|
1928
1975
|
// clear previous indexes
|
|
1929
1976
|
this.$indexes.clear();
|
|
1930
|
-
//
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1977
|
+
//
|
|
1978
|
+
// When decoding:
|
|
1979
|
+
// - enqueue items for DELETE callback.
|
|
1980
|
+
// - flag child items for garbage collection.
|
|
1981
|
+
//
|
|
1982
|
+
if (changes) {
|
|
1983
|
+
removeChildRefs.call(this, changes);
|
|
1935
1984
|
}
|
|
1936
1985
|
// clear items
|
|
1937
1986
|
this.$items.clear();
|
|
@@ -2012,36 +2061,6 @@ class SetSchema {
|
|
|
2012
2061
|
}
|
|
2013
2062
|
return cloned;
|
|
2014
2063
|
}
|
|
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
2064
|
}
|
|
2046
2065
|
|
|
2047
2066
|
class ClientState {
|
|
@@ -2064,6 +2083,75 @@ class ClientState {
|
|
|
2064
2083
|
}
|
|
2065
2084
|
}
|
|
2066
2085
|
|
|
2086
|
+
class ReferenceTracker {
|
|
2087
|
+
constructor() {
|
|
2088
|
+
//
|
|
2089
|
+
// Relation of refId => Schema structure
|
|
2090
|
+
// For direct access of structures during decoding time.
|
|
2091
|
+
//
|
|
2092
|
+
this.refs = new Map();
|
|
2093
|
+
this.refCounts = {};
|
|
2094
|
+
this.deletedRefs = new Set();
|
|
2095
|
+
this.nextUniqueId = 0;
|
|
2096
|
+
}
|
|
2097
|
+
getNextUniqueId() {
|
|
2098
|
+
return this.nextUniqueId++;
|
|
2099
|
+
}
|
|
2100
|
+
// for decoding
|
|
2101
|
+
addRef(refId, ref, incrementCount = true) {
|
|
2102
|
+
this.refs.set(refId, ref);
|
|
2103
|
+
if (incrementCount) {
|
|
2104
|
+
this.refCounts[refId] = (this.refCounts[refId] || 0) + 1;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
// for decoding
|
|
2108
|
+
removeRef(refId) {
|
|
2109
|
+
this.refCounts[refId] = this.refCounts[refId] - 1;
|
|
2110
|
+
this.deletedRefs.add(refId);
|
|
2111
|
+
}
|
|
2112
|
+
clearRefs() {
|
|
2113
|
+
this.refs.clear();
|
|
2114
|
+
this.deletedRefs.clear();
|
|
2115
|
+
this.refCounts = {};
|
|
2116
|
+
}
|
|
2117
|
+
// for decoding
|
|
2118
|
+
garbageCollectDeletedRefs() {
|
|
2119
|
+
this.deletedRefs.forEach((refId) => {
|
|
2120
|
+
//
|
|
2121
|
+
// Skip active references.
|
|
2122
|
+
//
|
|
2123
|
+
if (this.refCounts[refId] > 0) {
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
const ref = this.refs.get(refId);
|
|
2127
|
+
//
|
|
2128
|
+
// Ensure child schema instances have their references removed as well.
|
|
2129
|
+
//
|
|
2130
|
+
if (ref instanceof Schema) {
|
|
2131
|
+
for (const fieldName in ref['_definition'].schema) {
|
|
2132
|
+
if (typeof (ref['_definition'].schema[fieldName]) !== "string" &&
|
|
2133
|
+
ref[fieldName] &&
|
|
2134
|
+
ref[fieldName]['$changes']) {
|
|
2135
|
+
this.removeRef(ref[fieldName]['$changes'].refId);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
else {
|
|
2140
|
+
const definition = ref['$changes'].parent._definition;
|
|
2141
|
+
const type = definition.schema[definition.fieldsByIndex[ref['$changes'].parentIndex]];
|
|
2142
|
+
if (typeof (Object.values(type)[0]) === "function") {
|
|
2143
|
+
Array.from(ref.values())
|
|
2144
|
+
.forEach((child) => this.removeRef(child['$changes'].refId));
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
this.refs.delete(refId);
|
|
2148
|
+
delete this.refCounts[refId];
|
|
2149
|
+
});
|
|
2150
|
+
// clear deleted refs.
|
|
2151
|
+
this.deletedRefs.clear();
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2067
2155
|
class EncodeSchemaError extends Error {
|
|
2068
2156
|
}
|
|
2069
2157
|
function assertType(value, type, klass, field) {
|
|
@@ -2126,12 +2214,17 @@ class Schema {
|
|
|
2126
2214
|
// fix enumerability of fields for end-user
|
|
2127
2215
|
Object.defineProperties(this, {
|
|
2128
2216
|
$changes: {
|
|
2129
|
-
value: new ChangeTree(this, undefined, new
|
|
2217
|
+
value: new ChangeTree(this, undefined, new ReferenceTracker()),
|
|
2130
2218
|
enumerable: false,
|
|
2131
2219
|
writable: true
|
|
2132
2220
|
},
|
|
2133
|
-
$listeners: {
|
|
2134
|
-
|
|
2221
|
+
// $listeners: {
|
|
2222
|
+
// value: undefined,
|
|
2223
|
+
// enumerable: false,
|
|
2224
|
+
// writable: true
|
|
2225
|
+
// },
|
|
2226
|
+
$callbacks: {
|
|
2227
|
+
value: undefined,
|
|
2135
2228
|
enumerable: false,
|
|
2136
2229
|
writable: true
|
|
2137
2230
|
},
|
|
@@ -2154,26 +2247,43 @@ class Schema {
|
|
|
2154
2247
|
return (type['_definition'] &&
|
|
2155
2248
|
type['_definition'].schema !== undefined);
|
|
2156
2249
|
}
|
|
2250
|
+
onChange(callback) {
|
|
2251
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.REPLACE, callback);
|
|
2252
|
+
}
|
|
2253
|
+
onRemove(callback) {
|
|
2254
|
+
return addCallback((this.$callbacks || (this.$callbacks = [])), OPERATION.DELETE, callback);
|
|
2255
|
+
}
|
|
2157
2256
|
assign(props) {
|
|
2158
2257
|
Object.assign(this, props);
|
|
2159
2258
|
return this;
|
|
2160
2259
|
}
|
|
2161
2260
|
get _definition() { return this.constructor._definition; }
|
|
2261
|
+
/**
|
|
2262
|
+
* (Server-side): Flag a property to be encoded for the next patch.
|
|
2263
|
+
* @param instance Schema instance
|
|
2264
|
+
* @param property string representing the property name, or number representing the index of the property.
|
|
2265
|
+
* @param operation OPERATION to perform (detected automatically)
|
|
2266
|
+
*/
|
|
2267
|
+
setDirty(property, operation) {
|
|
2268
|
+
this.$changes.change(property, operation);
|
|
2269
|
+
}
|
|
2162
2270
|
listen(attr, callback) {
|
|
2163
|
-
if (!this.$
|
|
2164
|
-
this.$
|
|
2271
|
+
if (!this.$callbacks) {
|
|
2272
|
+
this.$callbacks = {};
|
|
2273
|
+
}
|
|
2274
|
+
if (!this.$callbacks[attr]) {
|
|
2275
|
+
this.$callbacks[attr] = [];
|
|
2165
2276
|
}
|
|
2166
|
-
this.$
|
|
2277
|
+
this.$callbacks[attr].push(callback);
|
|
2167
2278
|
// return un-register callback.
|
|
2168
|
-
return () => this.$
|
|
2279
|
+
return () => spliceOne(this.$callbacks[attr], this.$callbacks[attr].indexOf(callback));
|
|
2169
2280
|
}
|
|
2170
|
-
decode(bytes, it = { offset: 0 }, ref = this
|
|
2281
|
+
decode(bytes, it = { offset: 0 }, ref = this) {
|
|
2282
|
+
const allChanges = [];
|
|
2171
2283
|
const $root = this.$changes.root;
|
|
2172
2284
|
const totalBytes = bytes.length;
|
|
2173
2285
|
let refId = 0;
|
|
2174
|
-
let changes = [];
|
|
2175
2286
|
$root.refs.set(refId, this);
|
|
2176
|
-
allChanges.set(refId, changes);
|
|
2177
2287
|
while (it.offset < totalBytes) {
|
|
2178
2288
|
let byte = bytes[it.offset++];
|
|
2179
2289
|
if (byte == SWITCH_TO_STRUCTURE) {
|
|
@@ -2186,9 +2296,6 @@ class Schema {
|
|
|
2186
2296
|
throw new Error(`"refId" not found: ${refId}`);
|
|
2187
2297
|
}
|
|
2188
2298
|
ref = nextRef;
|
|
2189
|
-
// create empty list of changes for this refId.
|
|
2190
|
-
changes = [];
|
|
2191
|
-
allChanges.set(refId, changes);
|
|
2192
2299
|
continue;
|
|
2193
2300
|
}
|
|
2194
2301
|
const changeTree = ref['$changes'];
|
|
@@ -2202,7 +2309,7 @@ class Schema {
|
|
|
2202
2309
|
// The `.clear()` method is calling `$root.removeRef(refId)` for
|
|
2203
2310
|
// each item inside this collection
|
|
2204
2311
|
//
|
|
2205
|
-
ref.clear(
|
|
2312
|
+
ref.clear(allChanges);
|
|
2206
2313
|
continue;
|
|
2207
2314
|
}
|
|
2208
2315
|
const fieldIndex = (isSchema)
|
|
@@ -2272,9 +2379,8 @@ class Schema {
|
|
|
2272
2379
|
value = this.createTypeInstance(childType);
|
|
2273
2380
|
value.$changes.refId = refId;
|
|
2274
2381
|
if (previousValue) {
|
|
2275
|
-
value
|
|
2276
|
-
value
|
|
2277
|
-
value.$listeners = previousValue.$listeners;
|
|
2382
|
+
value.$callbacks = previousValue.$callbacks;
|
|
2383
|
+
// value.$listeners = previousValue.$listeners;
|
|
2278
2384
|
if (previousValue['$changes'].refId &&
|
|
2279
2385
|
refId !== previousValue['$changes'].refId) {
|
|
2280
2386
|
$root.removeRef(previousValue['$changes'].refId);
|
|
@@ -2300,40 +2406,29 @@ class Schema {
|
|
|
2300
2406
|
value.$changes.refId = refId;
|
|
2301
2407
|
// preserve schema callbacks
|
|
2302
2408
|
if (previousValue) {
|
|
2303
|
-
value
|
|
2304
|
-
value.onRemove = previousValue.onRemove;
|
|
2305
|
-
value.onChange = previousValue.onChange;
|
|
2409
|
+
value['$callbacks'] = previousValue['$callbacks'];
|
|
2306
2410
|
if (previousValue['$changes'].refId &&
|
|
2307
2411
|
refId !== previousValue['$changes'].refId) {
|
|
2308
2412
|
$root.removeRef(previousValue['$changes'].refId);
|
|
2309
2413
|
//
|
|
2310
2414
|
// Trigger onRemove if structure has been replaced.
|
|
2311
2415
|
//
|
|
2312
|
-
const deletes = [];
|
|
2313
2416
|
const entries = previousValue.entries();
|
|
2314
2417
|
let iter;
|
|
2315
2418
|
while ((iter = entries.next()) && !iter.done) {
|
|
2316
2419
|
const [key, value] = iter.value;
|
|
2317
|
-
|
|
2420
|
+
allChanges.push({
|
|
2421
|
+
refId,
|
|
2318
2422
|
op: OPERATION.DELETE,
|
|
2319
2423
|
field: key,
|
|
2320
2424
|
value: undefined,
|
|
2321
2425
|
previousValue: value,
|
|
2322
2426
|
});
|
|
2323
2427
|
}
|
|
2324
|
-
allChanges.set(previousValue['$changes'].refId, deletes);
|
|
2325
2428
|
}
|
|
2326
2429
|
}
|
|
2327
2430
|
$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
2431
|
}
|
|
2336
|
-
let hasChange = (previousValue !== value);
|
|
2337
2432
|
if (value !== null &&
|
|
2338
2433
|
value !== undefined) {
|
|
2339
2434
|
if (value['$changes']) {
|
|
@@ -2341,14 +2436,7 @@ class Schema {
|
|
|
2341
2436
|
}
|
|
2342
2437
|
if (ref instanceof Schema) {
|
|
2343
2438
|
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;
|
|
2439
|
+
// ref[`_${fieldName}`] = value;
|
|
2352
2440
|
}
|
|
2353
2441
|
else if (ref instanceof MapSchema) {
|
|
2354
2442
|
// const key = ref['$indexes'].get(field);
|
|
@@ -2362,19 +2450,20 @@ class Schema {
|
|
|
2362
2450
|
// ref[key] = value;
|
|
2363
2451
|
ref.setAt(fieldIndex, value);
|
|
2364
2452
|
}
|
|
2365
|
-
else if (ref instanceof CollectionSchema
|
|
2366
|
-
ref instanceof SetSchema) {
|
|
2453
|
+
else if (ref instanceof CollectionSchema) {
|
|
2367
2454
|
const index = ref.add(value);
|
|
2368
2455
|
ref['setIndex'](fieldIndex, index);
|
|
2369
2456
|
}
|
|
2457
|
+
else if (ref instanceof SetSchema) {
|
|
2458
|
+
const index = ref.add(value);
|
|
2459
|
+
if (index !== false) {
|
|
2460
|
+
ref['setIndex'](fieldIndex, index);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2370
2463
|
}
|
|
2371
|
-
if (
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
// this.onChange || ref.$listeners[field]
|
|
2375
|
-
// )
|
|
2376
|
-
) {
|
|
2377
|
-
changes.push({
|
|
2464
|
+
if (previousValue !== value) {
|
|
2465
|
+
allChanges.push({
|
|
2466
|
+
refId,
|
|
2378
2467
|
op: operation,
|
|
2379
2468
|
field: fieldName,
|
|
2380
2469
|
dynamicIndex,
|
|
@@ -2643,7 +2732,7 @@ class Schema {
|
|
|
2643
2732
|
//
|
|
2644
2733
|
// use cached bytes directly if is from Schema type.
|
|
2645
2734
|
//
|
|
2646
|
-
filteredBytes
|
|
2735
|
+
filteredBytes.push.apply(filteredBytes, changeTree.caches[fieldIndex] ?? []);
|
|
2647
2736
|
containerIndexes.add(fieldIndex);
|
|
2648
2737
|
}
|
|
2649
2738
|
else {
|
|
@@ -2651,7 +2740,7 @@ class Schema {
|
|
|
2651
2740
|
//
|
|
2652
2741
|
// use cached bytes if already has the field
|
|
2653
2742
|
//
|
|
2654
|
-
filteredBytes
|
|
2743
|
+
filteredBytes.push.apply(filteredBytes, changeTree.caches[fieldIndex] ?? []);
|
|
2655
2744
|
}
|
|
2656
2745
|
else {
|
|
2657
2746
|
//
|
|
@@ -2716,20 +2805,6 @@ class Schema {
|
|
|
2716
2805
|
}
|
|
2717
2806
|
return cloned;
|
|
2718
2807
|
}
|
|
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
2808
|
toJSON() {
|
|
2734
2809
|
const schema = this._definition.schema;
|
|
2735
2810
|
const deprecated = this._definition.deprecated;
|
|
@@ -2772,109 +2847,73 @@ class Schema {
|
|
|
2772
2847
|
instance.$changes.root = this.$changes.root;
|
|
2773
2848
|
return instance;
|
|
2774
2849
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
const
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2850
|
+
_triggerChanges(changes) {
|
|
2851
|
+
const uniqueRefIds = new Set();
|
|
2852
|
+
const $refs = this.$changes.root.refs;
|
|
2853
|
+
for (let i = 0; i < changes.length; i++) {
|
|
2854
|
+
const change = changes[i];
|
|
2855
|
+
const refId = change.refId;
|
|
2856
|
+
const ref = $refs.get(refId);
|
|
2857
|
+
const $callbacks = ref['$callbacks'];
|
|
2858
|
+
//
|
|
2859
|
+
// trigger onRemove on child structure.
|
|
2860
|
+
//
|
|
2861
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
|
|
2862
|
+
change.previousValue instanceof Schema) {
|
|
2863
|
+
change.previousValue['$callbacks']?.[OPERATION.DELETE]?.forEach(callback => callback());
|
|
2864
|
+
}
|
|
2865
|
+
// no callbacks defined, skip this structure!
|
|
2866
|
+
if (!$callbacks) {
|
|
2867
|
+
continue;
|
|
2868
|
+
}
|
|
2869
|
+
if (ref instanceof Schema) {
|
|
2870
|
+
if (!uniqueRefIds.has(refId)) {
|
|
2871
|
+
try {
|
|
2872
|
+
// trigger onChange
|
|
2873
|
+
$callbacks?.[OPERATION.REPLACE]?.forEach(callback => callback(changes));
|
|
2874
|
+
}
|
|
2875
|
+
catch (e) {
|
|
2876
|
+
Schema.onError(e);
|
|
2795
2877
|
}
|
|
2796
2878
|
}
|
|
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);
|
|
2879
|
+
try {
|
|
2880
|
+
$callbacks[change.field]?.forEach(callback => callback(change.value, change.previousValue));
|
|
2881
|
+
}
|
|
2882
|
+
catch (e) {
|
|
2883
|
+
Schema.onError(e);
|
|
2813
2884
|
}
|
|
2814
2885
|
}
|
|
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
|
-
}
|
|
2886
|
+
else {
|
|
2887
|
+
// is a collection of items
|
|
2888
|
+
if (change.op === OPERATION.ADD && change.previousValue === undefined) {
|
|
2889
|
+
// triger onAdd
|
|
2890
|
+
$callbacks[OPERATION.ADD]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
|
|
2891
|
+
}
|
|
2892
|
+
else if (change.op === OPERATION.DELETE) {
|
|
2849
2893
|
//
|
|
2850
|
-
//
|
|
2894
|
+
// FIXME: `previousValue` should always be available.
|
|
2895
|
+
// ADD + DELETE operations are still encoding DELETE operation.
|
|
2851
2896
|
//
|
|
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
|
-
}
|
|
2897
|
+
if (change.previousValue !== undefined) {
|
|
2898
|
+
// triger onRemove
|
|
2899
|
+
$callbacks[OPERATION.DELETE]?.forEach(callback => callback(change.previousValue, change.dynamicIndex ?? change.field));
|
|
2864
2900
|
}
|
|
2865
2901
|
}
|
|
2866
|
-
if (
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
}
|
|
2871
|
-
catch (e) {
|
|
2872
|
-
Schema.onError(e);
|
|
2873
|
-
}
|
|
2902
|
+
else if (change.op === OPERATION.DELETE_AND_ADD) {
|
|
2903
|
+
// triger onRemove
|
|
2904
|
+
if (change.previousValue !== undefined) {
|
|
2905
|
+
$callbacks[OPERATION.DELETE]?.forEach(callback => callback(change.previousValue, change.dynamicIndex ?? change.field));
|
|
2874
2906
|
}
|
|
2907
|
+
// triger onAdd
|
|
2908
|
+
$callbacks[OPERATION.ADD]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
|
|
2909
|
+
}
|
|
2910
|
+
// trigger onChange
|
|
2911
|
+
if (change.value !== change.previousValue) {
|
|
2912
|
+
$callbacks[OPERATION.REPLACE]?.forEach(callback => callback(change.value, change.dynamicIndex ?? change.field));
|
|
2875
2913
|
}
|
|
2876
2914
|
}
|
|
2877
|
-
|
|
2915
|
+
uniqueRefIds.add(refId);
|
|
2916
|
+
}
|
|
2878
2917
|
}
|
|
2879
2918
|
}
|
|
2880
2919
|
Schema._definition = SchemaDefinition.create();
|
|
@@ -2920,7 +2959,7 @@ function __decorate(decorators, target, key, desc) {
|
|
|
2920
2959
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2921
2960
|
}
|
|
2922
2961
|
|
|
2923
|
-
const reflectionContext = new Context();
|
|
2962
|
+
const reflectionContext = { context: new Context() };
|
|
2924
2963
|
/**
|
|
2925
2964
|
* Reflection
|
|
2926
2965
|
*/
|
|
@@ -3025,14 +3064,14 @@ class Reflection extends Schema {
|
|
|
3025
3064
|
refType = typeInfo[1];
|
|
3026
3065
|
}
|
|
3027
3066
|
if (fieldType === "ref") {
|
|
3028
|
-
type(refType, context)(schemaType.prototype, field.name);
|
|
3067
|
+
type(refType, { context })(schemaType.prototype, field.name);
|
|
3029
3068
|
}
|
|
3030
3069
|
else {
|
|
3031
|
-
type({ [fieldType]: refType }, context)(schemaType.prototype, field.name);
|
|
3070
|
+
type({ [fieldType]: refType }, { context })(schemaType.prototype, field.name);
|
|
3032
3071
|
}
|
|
3033
3072
|
}
|
|
3034
3073
|
else {
|
|
3035
|
-
type(field.type, context)(schemaType.prototype, field.name);
|
|
3074
|
+
type(field.type, { context })(schemaType.prototype, field.name);
|
|
3036
3075
|
}
|
|
3037
3076
|
});
|
|
3038
3077
|
});
|
|
@@ -3060,8 +3099,8 @@ __decorate([
|
|
|
3060
3099
|
type("number", reflectionContext)
|
|
3061
3100
|
], Reflection.prototype, "rootType", void 0);
|
|
3062
3101
|
|
|
3063
|
-
registerType("map", { constructor: MapSchema
|
|
3064
|
-
registerType("array", { constructor: ArraySchema
|
|
3102
|
+
registerType("map", { constructor: MapSchema });
|
|
3103
|
+
registerType("array", { constructor: ArraySchema });
|
|
3065
3104
|
registerType("set", { constructor: SetSchema });
|
|
3066
3105
|
registerType("collection", { constructor: CollectionSchema, });
|
|
3067
3106
|
|