@colyseus/schema 3.0.0-alpha.3 → 3.0.0-alpha.31
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 +131 -61
- package/build/cjs/index.js +966 -563
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +965 -562
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +966 -563
- package/lib/Metadata.d.ts +15 -4
- package/lib/Metadata.js +86 -18
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +2 -3
- package/lib/Reflection.js +24 -28
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +2 -2
- package/lib/Schema.js +28 -41
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +1 -21
- package/lib/annotations.js +73 -153
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +142 -0
- package/lib/bench_encode.js.map +1 -0
- package/lib/codegen/api.js +1 -2
- package/lib/codegen/api.js.map +1 -1
- package/lib/codegen/languages/cpp.js +1 -2
- package/lib/codegen/languages/cpp.js.map +1 -1
- package/lib/codegen/languages/csharp.js +1 -2
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +1 -2
- package/lib/codegen/languages/haxe.js.map +1 -1
- package/lib/codegen/languages/java.js +1 -2
- package/lib/codegen/languages/java.js.map +1 -1
- package/lib/codegen/languages/js.js +1 -2
- package/lib/codegen/languages/js.js.map +1 -1
- package/lib/codegen/languages/lua.js +1 -2
- package/lib/codegen/languages/lua.js.map +1 -1
- package/lib/codegen/languages/ts.js +1 -2
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +2 -3
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +3 -3
- package/lib/codegen/types.js.map +1 -1
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +52 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/DecodeOperation.d.ts +0 -1
- package/lib/decoder/DecodeOperation.js +23 -11
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +6 -7
- package/lib/decoder/Decoder.js +8 -8
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/decoder/ReferenceTracker.js +3 -2
- package/lib/decoder/ReferenceTracker.js.map +1 -1
- package/lib/decoder/strategy/RawChanges.js +1 -2
- package/lib/decoder/strategy/RawChanges.js.map +1 -1
- package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
- package/lib/decoder/strategy/StateCallbacks.js +75 -65
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +9 -19
- package/lib/encoder/ChangeTree.js +129 -145
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +1 -5
- package/lib/encoder/EncodeOperation.js +74 -58
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +10 -8
- package/lib/encoder/Encoder.js +89 -56
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +17 -0
- package/lib/encoder/Root.js +44 -0
- package/lib/encoder/Root.js.map +1 -0
- package/lib/encoder/StateView.d.ts +2 -2
- package/lib/encoder/StateView.js +49 -59
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/assert.d.ts +2 -1
- package/lib/encoding/assert.js +5 -5
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.js +21 -22
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +2 -2
- package/lib/encoding/encode.js +40 -39
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +2 -1
- package/lib/encoding/spec.js +1 -0
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +6 -3
- package/lib/index.js +18 -13
- package/lib/index.js.map +1 -1
- package/lib/types/TypeContext.d.ts +23 -0
- package/lib/types/TypeContext.js +102 -0
- package/lib/types/TypeContext.js.map +1 -0
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +6 -9
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/custom/CollectionSchema.js +1 -0
- package/lib/types/custom/CollectionSchema.js.map +1 -1
- package/lib/types/custom/MapSchema.js +5 -0
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/custom/SetSchema.js +1 -0
- package/lib/types/custom/SetSchema.js.map +1 -1
- package/lib/types/registry.js +3 -4
- package/lib/types/registry.js.map +1 -1
- package/lib/types/symbols.d.ts +1 -0
- package/lib/types/symbols.js +2 -1
- package/lib/types/symbols.js.map +1 -1
- package/lib/types/utils.js +1 -2
- package/lib/types/utils.js.map +1 -1
- package/lib/utils.js +3 -4
- package/lib/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/Metadata.ts +104 -26
- package/src/Reflection.ts +26 -28
- package/src/Schema.ts +35 -47
- package/src/annotations.ts +82 -176
- package/src/bench_encode.ts +121 -0
- package/src/debug.ts +56 -0
- package/src/decoder/DecodeOperation.ts +28 -11
- package/src/decoder/Decoder.ts +13 -11
- package/src/decoder/ReferenceTracker.ts +3 -2
- package/src/decoder/strategy/StateCallbacks.ts +152 -81
- package/src/encoder/ChangeTree.ts +147 -166
- package/src/encoder/EncodeOperation.ts +93 -70
- package/src/encoder/Encoder.ts +111 -65
- package/src/encoder/Root.ts +51 -0
- package/src/encoder/StateView.ts +53 -69
- package/src/encoding/assert.ts +4 -3
- package/src/encoding/decode.ts +1 -2
- package/src/encoding/encode.ts +25 -22
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +8 -14
- package/src/types/TypeContext.ts +122 -0
- package/src/types/custom/ArraySchema.ts +10 -2
- package/src/types/custom/CollectionSchema.ts +1 -0
- package/src/types/custom/MapSchema.ts +6 -0
- package/src/types/custom/SetSchema.ts +1 -0
- package/src/types/symbols.ts +2 -0
package/build/esm/index.mjs
CHANGED
|
@@ -23,6 +23,7 @@ var OPERATION;
|
|
|
23
23
|
OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
|
|
24
24
|
OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
|
|
25
25
|
OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
|
|
26
|
+
OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
|
|
26
27
|
})(OPERATION || (OPERATION = {}));
|
|
27
28
|
|
|
28
29
|
Symbol.metadata ??= Symbol.for("Symbol.metadata");
|
|
@@ -33,6 +34,7 @@ const $decoder = Symbol("$decoder");
|
|
|
33
34
|
const $filter = Symbol("$filter");
|
|
34
35
|
const $getByIndex = Symbol("$getByIndex");
|
|
35
36
|
const $deleteByIndex = Symbol("$deleteByIndex");
|
|
37
|
+
const $descriptors = Symbol("$descriptors");
|
|
36
38
|
/**
|
|
37
39
|
* Used to hold ChangeTree instances whitin the structures
|
|
38
40
|
*/
|
|
@@ -68,34 +70,67 @@ function getType(identifier) {
|
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
const Metadata = {
|
|
71
|
-
addField(metadata, index,
|
|
73
|
+
addField(metadata, index, name, type, descriptor) {
|
|
72
74
|
if (index > 64) {
|
|
73
|
-
throw new Error(`Can't define field '${
|
|
75
|
+
throw new Error(`Can't define field '${name}'.\nSchema instances may only have up to 64 fields.`);
|
|
74
76
|
}
|
|
75
|
-
metadata[
|
|
77
|
+
metadata[index] = Object.assign(metadata[index] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
|
|
76
78
|
{
|
|
77
79
|
type: (Array.isArray(type))
|
|
78
80
|
? { array: type[0] }
|
|
79
81
|
: type,
|
|
80
82
|
index,
|
|
81
|
-
|
|
83
|
+
name,
|
|
82
84
|
});
|
|
85
|
+
// create "descriptors" map
|
|
86
|
+
metadata[$descriptors] ??= {};
|
|
87
|
+
if (descriptor) {
|
|
88
|
+
// for encoder
|
|
89
|
+
metadata[$descriptors][name] = descriptor;
|
|
90
|
+
metadata[$descriptors][`_${name}`] = {
|
|
91
|
+
value: undefined,
|
|
92
|
+
writable: true,
|
|
93
|
+
enumerable: false,
|
|
94
|
+
configurable: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// for decoder
|
|
99
|
+
metadata[$descriptors][name] = {
|
|
100
|
+
value: undefined,
|
|
101
|
+
writable: true,
|
|
102
|
+
enumerable: true,
|
|
103
|
+
configurable: true,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
83
106
|
// map -1 as last field index
|
|
84
107
|
Object.defineProperty(metadata, -1, {
|
|
85
108
|
value: index,
|
|
86
109
|
enumerable: false,
|
|
87
110
|
configurable: true
|
|
88
111
|
});
|
|
89
|
-
// map
|
|
90
|
-
Object.defineProperty(metadata,
|
|
91
|
-
value:
|
|
112
|
+
// map field name => index (non enumerable)
|
|
113
|
+
Object.defineProperty(metadata, name, {
|
|
114
|
+
value: index,
|
|
92
115
|
enumerable: false,
|
|
93
116
|
configurable: true,
|
|
94
117
|
});
|
|
118
|
+
// if child Ref/complex type, add to -4
|
|
119
|
+
if (typeof (metadata[index].type) !== "string") {
|
|
120
|
+
if (metadata[-4] === undefined) {
|
|
121
|
+
Object.defineProperty(metadata, -4, {
|
|
122
|
+
value: [],
|
|
123
|
+
enumerable: false,
|
|
124
|
+
configurable: true,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
metadata[-4].push(index);
|
|
128
|
+
}
|
|
95
129
|
},
|
|
96
130
|
setTag(metadata, fieldName, tag) {
|
|
131
|
+
const index = metadata[fieldName];
|
|
132
|
+
const field = metadata[index];
|
|
97
133
|
// add 'tag' to the field
|
|
98
|
-
const field = metadata[fieldName];
|
|
99
134
|
field.tag = tag;
|
|
100
135
|
if (!metadata[-2]) {
|
|
101
136
|
// -2: all field indexes with "view" tag
|
|
@@ -111,20 +146,14 @@ const Metadata = {
|
|
|
111
146
|
configurable: true
|
|
112
147
|
});
|
|
113
148
|
}
|
|
114
|
-
metadata[-2].push(
|
|
149
|
+
metadata[-2].push(index);
|
|
115
150
|
if (!metadata[-3][tag]) {
|
|
116
151
|
metadata[-3][tag] = [];
|
|
117
152
|
}
|
|
118
|
-
metadata[-3][tag].push(
|
|
153
|
+
metadata[-3][tag].push(index);
|
|
119
154
|
},
|
|
120
155
|
setFields(target, fields) {
|
|
121
156
|
const metadata = (target.prototype.constructor[Symbol.metadata] ??= {});
|
|
122
|
-
// target[$track] = function (changeTree, index: number, operation: OPERATION = OPERATION.ADD) {
|
|
123
|
-
// changeTree.change(index, operation, encodeSchemaOperation);
|
|
124
|
-
// };
|
|
125
|
-
// target[$encoder] = encodeSchemaOperation;
|
|
126
|
-
// target[$decoder] = decodeSchemaOperation;
|
|
127
|
-
// if (!target.prototype.toJSON) { target.prototype.toJSON = Schema.prototype.toJSON; }
|
|
128
157
|
let index = 0;
|
|
129
158
|
for (const field in fields) {
|
|
130
159
|
const type = fields[field];
|
|
@@ -132,13 +161,53 @@ const Metadata = {
|
|
|
132
161
|
const complexTypeKlass = (Array.isArray(type))
|
|
133
162
|
? getType("array")
|
|
134
163
|
: (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
|
|
135
|
-
Metadata.addField(metadata, index, field, type, getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass
|
|
164
|
+
Metadata.addField(metadata, index, field, type, getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass));
|
|
136
165
|
index++;
|
|
137
166
|
}
|
|
138
167
|
},
|
|
139
168
|
isDeprecated(metadata, field) {
|
|
140
169
|
return metadata[field].deprecated === true;
|
|
141
170
|
},
|
|
171
|
+
init(klass) {
|
|
172
|
+
//
|
|
173
|
+
// Used only to initialize an empty Schema (Encoder#constructor)
|
|
174
|
+
// TODO: remove/refactor this...
|
|
175
|
+
//
|
|
176
|
+
const metadata = {};
|
|
177
|
+
klass[Symbol.metadata] = metadata;
|
|
178
|
+
Object.defineProperty(metadata, -1, {
|
|
179
|
+
value: 0,
|
|
180
|
+
enumerable: false,
|
|
181
|
+
configurable: true,
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
initialize(constructor, parentMetadata) {
|
|
185
|
+
let metadata = constructor[Symbol.metadata] ?? Object.create(null);
|
|
186
|
+
// make sure inherited classes have their own metadata object.
|
|
187
|
+
if (constructor[Symbol.metadata] === parentMetadata) {
|
|
188
|
+
metadata = Object.create(null);
|
|
189
|
+
if (parentMetadata) {
|
|
190
|
+
// assign parent metadata to current
|
|
191
|
+
Object.assign(metadata, parentMetadata);
|
|
192
|
+
for (let i = 0; i <= parentMetadata[-1]; i++) {
|
|
193
|
+
const fieldName = parentMetadata[i].name;
|
|
194
|
+
Object.defineProperty(metadata, fieldName, {
|
|
195
|
+
value: parentMetadata[fieldName],
|
|
196
|
+
enumerable: false,
|
|
197
|
+
configurable: true,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
Object.defineProperty(metadata, -1, {
|
|
201
|
+
value: parentMetadata[-1],
|
|
202
|
+
enumerable: false,
|
|
203
|
+
configurable: true,
|
|
204
|
+
writable: true,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
constructor[Symbol.metadata] = metadata;
|
|
209
|
+
return metadata;
|
|
210
|
+
},
|
|
142
211
|
isValidInstance(klass) {
|
|
143
212
|
return (klass.constructor[Symbol.metadata] &&
|
|
144
213
|
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], -1));
|
|
@@ -147,97 +216,68 @@ const Metadata = {
|
|
|
147
216
|
const metadata = klass[Symbol.metadata];
|
|
148
217
|
const fields = {};
|
|
149
218
|
for (let i = 0; i <= metadata[-1]; i++) {
|
|
150
|
-
fields[metadata[i]] = metadata[
|
|
219
|
+
fields[metadata[i].name] = metadata[i].type;
|
|
151
220
|
}
|
|
152
221
|
return fields;
|
|
153
222
|
}
|
|
154
223
|
};
|
|
155
224
|
|
|
156
225
|
var _a$5;
|
|
157
|
-
class Root {
|
|
158
|
-
constructor() {
|
|
159
|
-
this.nextUniqueId = 0;
|
|
160
|
-
this.refCount = new WeakMap();
|
|
161
|
-
// all changes
|
|
162
|
-
this.allChanges = new Map();
|
|
163
|
-
this.allFilteredChanges = new Map();
|
|
164
|
-
// pending changes to be encoded
|
|
165
|
-
this.changes = new Map();
|
|
166
|
-
this.filteredChanges = new Map();
|
|
167
|
-
}
|
|
168
|
-
getNextUniqueId() {
|
|
169
|
-
return this.nextUniqueId++;
|
|
170
|
-
}
|
|
171
|
-
add(changeTree) {
|
|
172
|
-
const refCount = this.refCount.get(changeTree) || 0;
|
|
173
|
-
this.refCount.set(changeTree, refCount + 1);
|
|
174
|
-
}
|
|
175
|
-
remove(changeTree) {
|
|
176
|
-
const refCount = this.refCount.get(changeTree);
|
|
177
|
-
if (refCount <= 1) {
|
|
178
|
-
this.allChanges.delete(changeTree);
|
|
179
|
-
this.changes.delete(changeTree);
|
|
180
|
-
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
181
|
-
this.allFilteredChanges.delete(changeTree);
|
|
182
|
-
this.filteredChanges.delete(changeTree);
|
|
183
|
-
}
|
|
184
|
-
this.refCount.delete(changeTree);
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
this.refCount.set(changeTree, refCount - 1);
|
|
188
|
-
}
|
|
189
|
-
changeTree.forEachChild((child, _) => this.remove(child));
|
|
190
|
-
}
|
|
191
|
-
clear() {
|
|
192
|
-
this.changes.clear();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
226
|
class ChangeTree {
|
|
196
227
|
static { _a$5 = $isNew; }
|
|
197
|
-
;
|
|
198
228
|
constructor(ref) {
|
|
199
|
-
this.
|
|
229
|
+
this.isFiltered = false;
|
|
230
|
+
this.isPartiallyFiltered = false;
|
|
200
231
|
this.currentOperationIndex = 0;
|
|
201
|
-
this.allChanges = new Map();
|
|
202
|
-
this.allFilteredChanges = new Map();
|
|
203
232
|
this.changes = new Map();
|
|
204
|
-
this.
|
|
233
|
+
this.allChanges = new Map();
|
|
205
234
|
this[_a$5] = true;
|
|
206
235
|
this.ref = ref;
|
|
236
|
+
//
|
|
237
|
+
// Does this structure have "filters" declared?
|
|
238
|
+
//
|
|
239
|
+
if (ref.constructor[Symbol.metadata]?.[-2]) {
|
|
240
|
+
this.allFilteredChanges = new Map();
|
|
241
|
+
this.filteredChanges = new Map();
|
|
242
|
+
}
|
|
207
243
|
}
|
|
208
244
|
setRoot(root) {
|
|
209
245
|
this.root = root;
|
|
210
246
|
this.root.add(this);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
247
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
248
|
+
if (this.root.types.hasFilters) {
|
|
249
|
+
//
|
|
250
|
+
// At Schema initialization, the "root" structure might not be available
|
|
251
|
+
// yet, as it only does once the "Encoder" has been set up.
|
|
252
|
+
//
|
|
253
|
+
// So the "parent" may be already set without a "root".
|
|
254
|
+
//
|
|
255
|
+
this.checkIsFiltered(metadata, this.parent, this.parentIndex);
|
|
256
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
257
|
+
this.root.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
258
|
+
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
220
261
|
if (!this.isFiltered) {
|
|
221
262
|
this.root.changes.set(this, this.changes);
|
|
263
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
222
264
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
265
|
+
this.ensureRefId();
|
|
266
|
+
if (metadata) {
|
|
267
|
+
metadata[-4]?.forEach((index) => {
|
|
268
|
+
const field = metadata[index];
|
|
269
|
+
const value = this.ref[field.name];
|
|
270
|
+
if (value) {
|
|
271
|
+
value[$changes].setRoot(root);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
228
274
|
}
|
|
229
|
-
if (
|
|
230
|
-
|
|
275
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
276
|
+
// MapSchema / ArraySchema, etc.
|
|
277
|
+
this.ref.forEach((value, key) => {
|
|
278
|
+
value[$changes].setRoot(root);
|
|
279
|
+
});
|
|
231
280
|
}
|
|
232
|
-
this.forEachChild((changeTree, _) => {
|
|
233
|
-
changeTree.setRoot(root);
|
|
234
|
-
});
|
|
235
|
-
// this.allChanges.forEach((_, index) => {
|
|
236
|
-
// const childRef = this.ref[$getByIndex](index);
|
|
237
|
-
// if (childRef && childRef[$changes]) {
|
|
238
|
-
// childRef[$changes].setRoot(root);
|
|
239
|
-
// }
|
|
240
|
-
// });
|
|
241
281
|
}
|
|
242
282
|
setParent(parent, root, parentIndex) {
|
|
243
283
|
this.parent = parent;
|
|
@@ -247,50 +287,60 @@ class ChangeTree {
|
|
|
247
287
|
return;
|
|
248
288
|
}
|
|
249
289
|
root.add(this);
|
|
290
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
250
291
|
// skip if parent is already set
|
|
251
|
-
if (root
|
|
252
|
-
this.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
292
|
+
if (root !== this.root) {
|
|
293
|
+
this.root = root;
|
|
294
|
+
if (root.types.hasFilters) {
|
|
295
|
+
this.checkIsFiltered(metadata, parent, parentIndex);
|
|
296
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
297
|
+
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
298
|
+
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!this.isFiltered) {
|
|
302
|
+
this.root.changes.set(this, this.changes);
|
|
303
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
304
|
+
}
|
|
305
|
+
this.ensureRefId();
|
|
261
306
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
307
|
+
// assign same parent on child structures
|
|
308
|
+
if (metadata) {
|
|
309
|
+
metadata[-4]?.forEach((index) => {
|
|
310
|
+
const field = metadata[index];
|
|
311
|
+
const value = this.ref[field.name];
|
|
312
|
+
value?.[$changes].setParent(this.ref, root, index);
|
|
313
|
+
// console.log(this.ref.constructor.name, field.name, value);
|
|
314
|
+
// try { throw new Error(); } catch (e) {
|
|
315
|
+
// console.log(e.stack);
|
|
316
|
+
// }
|
|
317
|
+
});
|
|
265
318
|
}
|
|
266
|
-
else {
|
|
267
|
-
|
|
319
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
320
|
+
// MapSchema / ArraySchema, etc.
|
|
321
|
+
this.ref.forEach((value, key) => {
|
|
322
|
+
value[$changes].setParent(this.ref, root, this.indexes[key] ?? key);
|
|
323
|
+
});
|
|
268
324
|
}
|
|
269
|
-
this.ensureRefId();
|
|
270
|
-
this.forEachChild((changeTree, atIndex) => {
|
|
271
|
-
changeTree.setParent(this.ref, root, atIndex);
|
|
272
|
-
});
|
|
273
325
|
}
|
|
274
326
|
forEachChild(callback) {
|
|
275
327
|
//
|
|
276
328
|
// assign same parent on child structures
|
|
277
329
|
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const value = this.ref[field];
|
|
283
|
-
if (value
|
|
284
|
-
callback(value[$changes],
|
|
330
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
331
|
+
if (metadata) {
|
|
332
|
+
metadata[-4]?.forEach((index) => {
|
|
333
|
+
const field = metadata[index];
|
|
334
|
+
const value = this.ref[field.name];
|
|
335
|
+
if (value) {
|
|
336
|
+
callback(value[$changes], index);
|
|
285
337
|
}
|
|
286
|
-
}
|
|
338
|
+
});
|
|
287
339
|
}
|
|
288
|
-
else if (typeof (this.ref)
|
|
340
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
289
341
|
// MapSchema / ArraySchema, etc.
|
|
290
342
|
this.ref.forEach((value, key) => {
|
|
291
|
-
|
|
292
|
-
callback(value[$changes], this.ref[$changes].indexes[key]);
|
|
293
|
-
}
|
|
343
|
+
callback(value[$changes], this.indexes[key] ?? key);
|
|
294
344
|
});
|
|
295
345
|
}
|
|
296
346
|
}
|
|
@@ -299,8 +349,8 @@ class ChangeTree {
|
|
|
299
349
|
this.root?.changes.set(this, this.changes);
|
|
300
350
|
}
|
|
301
351
|
change(index, operation = OPERATION.ADD) {
|
|
302
|
-
const metadata = this.ref
|
|
303
|
-
const isFiltered = this.isFiltered || (metadata
|
|
352
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
353
|
+
const isFiltered = this.isFiltered || (metadata?.[index]?.tag !== undefined);
|
|
304
354
|
const changeSet = (isFiltered)
|
|
305
355
|
? this.filteredChanges
|
|
306
356
|
: this.changes;
|
|
@@ -311,14 +361,15 @@ class ChangeTree {
|
|
|
311
361
|
: (previousOperation === OPERATION.DELETE)
|
|
312
362
|
? OPERATION.DELETE_AND_ADD
|
|
313
363
|
: operation;
|
|
364
|
+
//
|
|
365
|
+
// TODO: are DELETE operations being encoded as ADD here ??
|
|
366
|
+
//
|
|
314
367
|
changeSet.set(index, op);
|
|
315
368
|
}
|
|
316
|
-
//
|
|
317
|
-
// TODO: are DELETE operations being encoded as ADD here ??
|
|
318
|
-
//
|
|
319
369
|
if (isFiltered) {
|
|
320
370
|
this.allFilteredChanges.set(index, OPERATION.ADD);
|
|
321
371
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
372
|
+
this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
322
373
|
}
|
|
323
374
|
else {
|
|
324
375
|
this.allChanges.set(index, OPERATION.ADD);
|
|
@@ -357,7 +408,6 @@ class ChangeTree {
|
|
|
357
408
|
}
|
|
358
409
|
_shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
|
|
359
410
|
Array.from(allChangeSet.entries()).forEach(([index, op]) => {
|
|
360
|
-
// console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
|
|
361
411
|
if (index >= startIndex) {
|
|
362
412
|
allChangeSet.delete(index);
|
|
363
413
|
allChangeSet.set(index + shiftIndex, op);
|
|
@@ -365,9 +415,7 @@ class ChangeTree {
|
|
|
365
415
|
});
|
|
366
416
|
}
|
|
367
417
|
indexedOperation(index, operation, allChangesIndex = index) {
|
|
368
|
-
|
|
369
|
-
const isFiltered = this.isFiltered || (metadata && metadata[metadata[index]].tag !== undefined);
|
|
370
|
-
if (isFiltered) {
|
|
418
|
+
if (this.filteredChanges !== undefined) {
|
|
371
419
|
this.allFilteredChanges.set(allChangesIndex, OPERATION.ADD);
|
|
372
420
|
this.filteredChanges.set(index, operation);
|
|
373
421
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
@@ -380,8 +428,8 @@ class ChangeTree {
|
|
|
380
428
|
}
|
|
381
429
|
getType(index) {
|
|
382
430
|
if (Metadata.isValidInstance(this.ref)) {
|
|
383
|
-
const metadata = this.ref
|
|
384
|
-
return metadata[
|
|
431
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
432
|
+
return metadata[index].type;
|
|
385
433
|
}
|
|
386
434
|
else {
|
|
387
435
|
//
|
|
@@ -395,7 +443,7 @@ class ChangeTree {
|
|
|
395
443
|
}
|
|
396
444
|
getChange(index) {
|
|
397
445
|
// TODO: optimize this. avoid checking against multiple instances
|
|
398
|
-
return this.changes.get(index) ?? this.filteredChanges
|
|
446
|
+
return this.changes.get(index) ?? this.filteredChanges?.get(index);
|
|
399
447
|
}
|
|
400
448
|
//
|
|
401
449
|
// used during `.encode()`
|
|
@@ -416,9 +464,7 @@ class ChangeTree {
|
|
|
416
464
|
}
|
|
417
465
|
return;
|
|
418
466
|
}
|
|
419
|
-
const
|
|
420
|
-
const isFiltered = this.isFiltered || (metadata && metadata[metadata[index]].tag !== undefined);
|
|
421
|
-
const changeSet = (isFiltered)
|
|
467
|
+
const changeSet = (this.filteredChanges)
|
|
422
468
|
? this.filteredChanges
|
|
423
469
|
: this.changes;
|
|
424
470
|
const previousValue = this.getValue(index);
|
|
@@ -441,7 +487,7 @@ class ChangeTree {
|
|
|
441
487
|
//
|
|
442
488
|
// FIXME: this is looking a bit ugly (and repeated from `.change()`)
|
|
443
489
|
//
|
|
444
|
-
if (
|
|
490
|
+
if (this.filteredChanges) {
|
|
445
491
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
446
492
|
this.allFilteredChanges.delete(allChangesIndex);
|
|
447
493
|
}
|
|
@@ -452,6 +498,7 @@ class ChangeTree {
|
|
|
452
498
|
}
|
|
453
499
|
endEncode() {
|
|
454
500
|
this.changes.clear();
|
|
501
|
+
// ArraySchema and MapSchema have a custom "encode end" method
|
|
455
502
|
this.ref[$onEncodeEnd]?.();
|
|
456
503
|
// Not a new instance anymore
|
|
457
504
|
delete this[$isNew];
|
|
@@ -464,12 +511,12 @@ class ChangeTree {
|
|
|
464
511
|
//
|
|
465
512
|
this.ref[$onEncodeEnd]?.();
|
|
466
513
|
this.changes.clear();
|
|
467
|
-
this.filteredChanges
|
|
514
|
+
this.filteredChanges?.clear();
|
|
468
515
|
// reset operation index
|
|
469
516
|
this.currentOperationIndex = 0;
|
|
470
517
|
if (discardAll) {
|
|
471
518
|
this.allChanges.clear();
|
|
472
|
-
this.allFilteredChanges
|
|
519
|
+
this.allFilteredChanges?.clear();
|
|
473
520
|
// remove children references
|
|
474
521
|
this.forEachChild((changeTree, _) => this.root?.remove(changeTree));
|
|
475
522
|
}
|
|
@@ -496,33 +543,41 @@ class ChangeTree {
|
|
|
496
543
|
get changed() {
|
|
497
544
|
return this.changes.size > 0;
|
|
498
545
|
}
|
|
499
|
-
checkIsFiltered(parent, parentIndex) {
|
|
546
|
+
checkIsFiltered(metadata, parent, parentIndex) {
|
|
500
547
|
// Detect if current structure has "filters" declared
|
|
501
|
-
this.isPartiallyFiltered =
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const metadata = parent['constructor'][Symbol.metadata];
|
|
506
|
-
const fieldName = metadata?.[parentIndex];
|
|
507
|
-
const isParentOwned = metadata?.[fieldName]?.tag !== undefined;
|
|
508
|
-
this.isFiltered = isParentOwned || parent[$changes].isFiltered; // metadata?.[-2]
|
|
509
|
-
parent = parent[$changes].parent;
|
|
548
|
+
this.isPartiallyFiltered = metadata?.[-2] !== undefined;
|
|
549
|
+
if (this.isPartiallyFiltered) {
|
|
550
|
+
this.filteredChanges = this.filteredChanges || new Map();
|
|
551
|
+
this.allFilteredChanges = this.allFilteredChanges || new Map();
|
|
510
552
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
//
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
this.
|
|
553
|
+
if (parent) {
|
|
554
|
+
if (!Metadata.isValidInstance(parent)) {
|
|
555
|
+
const parentChangeTree = parent[$changes];
|
|
556
|
+
parent = parentChangeTree.parent;
|
|
557
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
558
|
+
}
|
|
559
|
+
const parentMetadata = parent?.constructor?.[Symbol.metadata];
|
|
560
|
+
this.isFiltered = (parent && parentMetadata?.[-2]?.includes(parentIndex));
|
|
561
|
+
//
|
|
562
|
+
// TODO: refactor this!
|
|
563
|
+
//
|
|
564
|
+
// swapping `changes` and `filteredChanges` is required here
|
|
565
|
+
// because "isFiltered" may not be imedialely available on `change()`
|
|
566
|
+
//
|
|
567
|
+
if (this.isFiltered) {
|
|
568
|
+
this.filteredChanges = new Map();
|
|
569
|
+
this.allFilteredChanges = new Map();
|
|
570
|
+
if (this.changes.size > 0) {
|
|
571
|
+
// swap changes reference
|
|
572
|
+
const changes = this.changes;
|
|
573
|
+
this.changes = this.filteredChanges;
|
|
574
|
+
this.filteredChanges = changes;
|
|
575
|
+
// swap "all changes" reference
|
|
576
|
+
const allFilteredChanges = this.allFilteredChanges;
|
|
577
|
+
this.allFilteredChanges = this.allChanges;
|
|
578
|
+
this.allChanges = allFilteredChanges;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
526
581
|
}
|
|
527
582
|
}
|
|
528
583
|
}
|
|
@@ -559,26 +614,29 @@ try {
|
|
|
559
614
|
textEncoder = new TextEncoder();
|
|
560
615
|
}
|
|
561
616
|
catch (e) { }
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
617
|
+
const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
|
|
618
|
+
const utf8Length = (hasBufferByteLength)
|
|
619
|
+
? Buffer.byteLength // node
|
|
620
|
+
: function (str, _) {
|
|
621
|
+
var c = 0, length = 0;
|
|
622
|
+
for (var i = 0, l = str.length; i < l; i++) {
|
|
623
|
+
c = str.charCodeAt(i);
|
|
624
|
+
if (c < 0x80) {
|
|
625
|
+
length += 1;
|
|
626
|
+
}
|
|
627
|
+
else if (c < 0x800) {
|
|
628
|
+
length += 2;
|
|
629
|
+
}
|
|
630
|
+
else if (c < 0xd800 || c >= 0xe000) {
|
|
631
|
+
length += 3;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
i++;
|
|
635
|
+
length += 4;
|
|
636
|
+
}
|
|
578
637
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
638
|
+
return length;
|
|
639
|
+
};
|
|
582
640
|
function utf8Write(view, str, it) {
|
|
583
641
|
var c = 0;
|
|
584
642
|
for (var i = 0, l = str.length; i < l; i++) {
|
|
@@ -673,8 +731,7 @@ function string$1(bytes, value, it) {
|
|
|
673
731
|
if (!value) {
|
|
674
732
|
value = "";
|
|
675
733
|
}
|
|
676
|
-
|
|
677
|
-
let length = Buffer.byteLength(value, "utf8");
|
|
734
|
+
let length = utf8Length(value, "utf8");
|
|
678
735
|
let size = 0;
|
|
679
736
|
// fixstr
|
|
680
737
|
if (length < 0x20) {
|
|
@@ -785,81 +842,40 @@ function number$1(bytes, value, it) {
|
|
|
785
842
|
|
|
786
843
|
var encode = /*#__PURE__*/Object.freeze({
|
|
787
844
|
__proto__: null,
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
uint8: uint8$1,
|
|
845
|
+
boolean: boolean$1,
|
|
846
|
+
float32: float32$1,
|
|
847
|
+
float64: float64$1,
|
|
792
848
|
int16: int16$1,
|
|
793
|
-
uint16: uint16$1,
|
|
794
849
|
int32: int32$1,
|
|
795
|
-
uint32: uint32$1,
|
|
796
850
|
int64: int64$1,
|
|
851
|
+
int8: int8$1,
|
|
852
|
+
number: number$1,
|
|
853
|
+
string: string$1,
|
|
854
|
+
uint16: uint16$1,
|
|
855
|
+
uint32: uint32$1,
|
|
797
856
|
uint64: uint64$1,
|
|
798
|
-
|
|
799
|
-
|
|
857
|
+
uint8: uint8$1,
|
|
858
|
+
utf8Length: utf8Length,
|
|
859
|
+
utf8Write: utf8Write,
|
|
800
860
|
writeFloat32: writeFloat32,
|
|
801
|
-
writeFloat64: writeFloat64
|
|
802
|
-
boolean: boolean$1,
|
|
803
|
-
string: string$1,
|
|
804
|
-
number: number$1
|
|
861
|
+
writeFloat64: writeFloat64
|
|
805
862
|
});
|
|
806
863
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
case "int32":
|
|
819
|
-
case "uint32":
|
|
820
|
-
case "int64":
|
|
821
|
-
case "uint64":
|
|
822
|
-
case "float32":
|
|
823
|
-
case "float64":
|
|
824
|
-
typeofTarget = "number";
|
|
825
|
-
if (isNaN(value)) {
|
|
826
|
-
console.log(`trying to encode "NaN" in ${klass.constructor.name}#${field}`);
|
|
827
|
-
}
|
|
828
|
-
break;
|
|
829
|
-
case "string":
|
|
830
|
-
typeofTarget = "string";
|
|
831
|
-
allowNull = true;
|
|
832
|
-
break;
|
|
833
|
-
case "boolean":
|
|
834
|
-
// boolean is always encoded as true/false based on truthiness
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) {
|
|
838
|
-
let foundValue = `'${JSON.stringify(value)}'${(value && value.constructor && ` (${value.constructor.name})`) || ''}`;
|
|
839
|
-
throw new EncodeSchemaError(`a '${typeofTarget}' was expected, but ${foundValue} was provided in ${klass.constructor.name}#${field}`);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
function assertInstanceType(value, type, klass, field) {
|
|
843
|
-
if (!(value instanceof type)) {
|
|
844
|
-
throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && value.constructor.name}' was provided in ${klass.constructor.name}#${field}`);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
function encodePrimitiveType(type, bytes, value, klass, field, it) {
|
|
849
|
-
assertType(value, type, klass, field);
|
|
850
|
-
const encodeFunc = encode[type];
|
|
851
|
-
if (encodeFunc) {
|
|
852
|
-
encodeFunc(bytes, value, it);
|
|
853
|
-
// encodeFunc(bytes, value);
|
|
854
|
-
}
|
|
855
|
-
else {
|
|
856
|
-
throw new EncodeSchemaError(`a '${type}' was expected, but ${value} was provided in ${klass.constructor.name}#${field}`);
|
|
864
|
+
function encodeValue(encoder, bytes,
|
|
865
|
+
// ref: Ref,
|
|
866
|
+
type, value,
|
|
867
|
+
// field: string | number,
|
|
868
|
+
operation, it) {
|
|
869
|
+
if (typeof (type) === "string") {
|
|
870
|
+
//
|
|
871
|
+
// Primitive values
|
|
872
|
+
//
|
|
873
|
+
// assertType(value, type as string, ref as Schema, field);
|
|
874
|
+
encode[type]?.(bytes, value, it);
|
|
857
875
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
// TODO: move this to the `@type()` annotation
|
|
862
|
-
assertInstanceType(value, type, ref, field);
|
|
876
|
+
else if (type[Symbol.metadata] !== undefined) {
|
|
877
|
+
// // TODO: move this to the `@type()` annotation
|
|
878
|
+
// assertInstanceType(value, type as typeof Schema, ref as Schema, field);
|
|
863
879
|
//
|
|
864
880
|
// Encode refId for this instance.
|
|
865
881
|
// The actual instance is going to be encoded on next `changeTree` iteration.
|
|
@@ -870,21 +886,15 @@ function encodeValue(encoder, bytes, ref, type, value, field, operation, it) {
|
|
|
870
886
|
encoder.tryEncodeTypeId(bytes, type, value.constructor, it);
|
|
871
887
|
}
|
|
872
888
|
}
|
|
873
|
-
else if (typeof (type) === "string") {
|
|
874
|
-
//
|
|
875
|
-
// Primitive values
|
|
876
|
-
//
|
|
877
|
-
encodePrimitiveType(type, bytes, value, ref, field, it);
|
|
878
|
-
}
|
|
879
889
|
else {
|
|
880
|
-
//
|
|
881
|
-
// Custom type (MapSchema, ArraySchema, etc)
|
|
882
|
-
//
|
|
883
|
-
const definition = getType(Object.keys(type)[0]);
|
|
884
|
-
//
|
|
885
|
-
// ensure a ArraySchema has been provided
|
|
886
|
-
//
|
|
887
|
-
assertInstanceType(ref[field], definition.constructor, ref, field);
|
|
890
|
+
// //
|
|
891
|
+
// // Custom type (MapSchema, ArraySchema, etc)
|
|
892
|
+
// //
|
|
893
|
+
// const definition = getType(Object.keys(type)[0]);
|
|
894
|
+
// //
|
|
895
|
+
// // ensure a ArraySchema has been provided
|
|
896
|
+
// //
|
|
897
|
+
// assertInstanceType(ref[field], definition.constructor, ref as Schema, field);
|
|
888
898
|
//
|
|
889
899
|
// Encode refId for this instance.
|
|
890
900
|
// The actual instance is going to be encoded on next `changeTree` iteration.
|
|
@@ -897,26 +907,27 @@ function encodeValue(encoder, bytes, ref, type, value, field, operation, it) {
|
|
|
897
907
|
* @private
|
|
898
908
|
*/
|
|
899
909
|
const encodeSchemaOperation = function (encoder, bytes, changeTree, index, operation, it) {
|
|
900
|
-
const ref = changeTree.ref;
|
|
901
|
-
const metadata = ref['constructor'][Symbol.metadata];
|
|
902
|
-
const field = metadata[index];
|
|
903
|
-
const type = metadata[field].type;
|
|
904
|
-
const value = ref[field];
|
|
905
910
|
// "compress" field index + operation
|
|
906
911
|
bytes[it.offset++] = (index | operation) & 255;
|
|
907
912
|
// Do not encode value for DELETE operations
|
|
908
913
|
if (operation === OPERATION.DELETE) {
|
|
909
914
|
return;
|
|
910
915
|
}
|
|
916
|
+
const ref = changeTree.ref;
|
|
917
|
+
const metadata = ref['constructor'][Symbol.metadata];
|
|
918
|
+
const field = metadata[index];
|
|
911
919
|
// TODO: inline this function call small performance gain
|
|
912
|
-
encodeValue(encoder, bytes,
|
|
920
|
+
encodeValue(encoder, bytes,
|
|
921
|
+
// ref,
|
|
922
|
+
metadata[index].type, ref[field.name],
|
|
923
|
+
// index,
|
|
924
|
+
operation, it);
|
|
913
925
|
};
|
|
914
926
|
/**
|
|
915
927
|
* Used for collections (MapSchema, CollectionSchema, SetSchema)
|
|
916
928
|
* @private
|
|
917
929
|
*/
|
|
918
|
-
const encodeKeyValueOperation = function (encoder, bytes, changeTree,
|
|
919
|
-
const ref = changeTree.ref;
|
|
930
|
+
const encodeKeyValueOperation = function (encoder, bytes, changeTree, index, operation, it) {
|
|
920
931
|
// encode operation
|
|
921
932
|
bytes[it.offset++] = operation & 255;
|
|
922
933
|
// custom operations
|
|
@@ -924,11 +935,12 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
924
935
|
return;
|
|
925
936
|
}
|
|
926
937
|
// encode index
|
|
927
|
-
number$1(bytes,
|
|
938
|
+
number$1(bytes, index, it);
|
|
928
939
|
// Do not encode value for DELETE operations
|
|
929
940
|
if (operation === OPERATION.DELETE) {
|
|
930
941
|
return;
|
|
931
942
|
}
|
|
943
|
+
const ref = changeTree.ref;
|
|
932
944
|
//
|
|
933
945
|
// encode "alias" for dynamic fields (maps)
|
|
934
946
|
//
|
|
@@ -937,14 +949,30 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
937
949
|
//
|
|
938
950
|
// MapSchema dynamic key
|
|
939
951
|
//
|
|
940
|
-
const dynamicIndex = changeTree.ref['$indexes'].get(
|
|
952
|
+
const dynamicIndex = changeTree.ref['$indexes'].get(index);
|
|
941
953
|
string$1(bytes, dynamicIndex, it);
|
|
942
954
|
}
|
|
943
955
|
}
|
|
944
|
-
const type = changeTree.getType(
|
|
945
|
-
const value = changeTree.getValue(
|
|
956
|
+
const type = changeTree.getType(index);
|
|
957
|
+
const value = changeTree.getValue(index);
|
|
958
|
+
// try { throw new Error(); } catch (e) {
|
|
959
|
+
// // only print if not coming from Reflection.ts
|
|
960
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
961
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
962
|
+
// ref: changeTree.ref.constructor.name,
|
|
963
|
+
// field,
|
|
964
|
+
// operation: OPERATION[operation],
|
|
965
|
+
// value: value?.toJSON(),
|
|
966
|
+
// items: ref.toJSON(),
|
|
967
|
+
// });
|
|
968
|
+
// }
|
|
969
|
+
// }
|
|
946
970
|
// TODO: inline this function call small performance gain
|
|
947
|
-
encodeValue(encoder, bytes,
|
|
971
|
+
encodeValue(encoder, bytes,
|
|
972
|
+
// ref,
|
|
973
|
+
type, value,
|
|
974
|
+
// index,
|
|
975
|
+
operation, it);
|
|
948
976
|
};
|
|
949
977
|
/**
|
|
950
978
|
* Used for collections (MapSchema, ArraySchema, etc.)
|
|
@@ -952,15 +980,19 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
952
980
|
*/
|
|
953
981
|
const encodeArray = function (encoder, bytes, changeTree, field, operation, it, isEncodeAll, hasView) {
|
|
954
982
|
const ref = changeTree.ref;
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
983
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
|
|
984
|
+
let refOrIndex;
|
|
985
|
+
if (useOperationByRefId) {
|
|
986
|
+
refOrIndex = ref['tmpItems'][field][$changes].refId;
|
|
987
|
+
if (operation === OPERATION.DELETE) {
|
|
988
|
+
operation = OPERATION.DELETE_BY_REFID;
|
|
989
|
+
}
|
|
990
|
+
else if (operation === OPERATION.ADD) {
|
|
991
|
+
operation = OPERATION.ADD_BY_REFID;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
refOrIndex = field;
|
|
964
996
|
}
|
|
965
997
|
// encode operation
|
|
966
998
|
bytes[it.offset++] = operation & 255;
|
|
@@ -969,7 +1001,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
969
1001
|
return;
|
|
970
1002
|
}
|
|
971
1003
|
// encode index
|
|
972
|
-
number$1(bytes,
|
|
1004
|
+
number$1(bytes, refOrIndex, it);
|
|
973
1005
|
// Do not encode value for DELETE operations
|
|
974
1006
|
if (operation === OPERATION.DELETE) {
|
|
975
1007
|
return;
|
|
@@ -984,7 +1016,11 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
984
1016
|
// items: ref.toJSON(),
|
|
985
1017
|
// });
|
|
986
1018
|
// TODO: inline this function call small performance gain
|
|
987
|
-
encodeValue(encoder, bytes,
|
|
1019
|
+
encodeValue(encoder, bytes,
|
|
1020
|
+
// ref,
|
|
1021
|
+
type, value,
|
|
1022
|
+
// field,
|
|
1023
|
+
operation, it);
|
|
988
1024
|
};
|
|
989
1025
|
|
|
990
1026
|
/**
|
|
@@ -1010,7 +1046,6 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
1010
1046
|
* SOFTWARE
|
|
1011
1047
|
*/
|
|
1012
1048
|
function utf8Read(bytes, it, length) {
|
|
1013
|
-
it.offset += length;
|
|
1014
1049
|
var string = '', chr = 0;
|
|
1015
1050
|
for (var i = it.offset, end = it.offset + length; i < end; i++) {
|
|
1016
1051
|
var byte = bytes[i];
|
|
@@ -1047,6 +1082,7 @@ function utf8Read(bytes, it, length) {
|
|
|
1047
1082
|
// (do not throw error to avoid server/client from crashing due to hack attemps)
|
|
1048
1083
|
// throw new Error('Invalid byte ' + byte.toString(16));
|
|
1049
1084
|
}
|
|
1085
|
+
it.offset += length;
|
|
1050
1086
|
return string;
|
|
1051
1087
|
}
|
|
1052
1088
|
function int8(bytes, it) {
|
|
@@ -1218,31 +1254,31 @@ function switchStructureCheck(bytes, it) {
|
|
|
1218
1254
|
|
|
1219
1255
|
var decode = /*#__PURE__*/Object.freeze({
|
|
1220
1256
|
__proto__: null,
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
uint8: uint8,
|
|
1224
|
-
int16: int16,
|
|
1225
|
-
uint16: uint16,
|
|
1226
|
-
int32: int32,
|
|
1227
|
-
uint32: uint32,
|
|
1257
|
+
arrayCheck: arrayCheck,
|
|
1258
|
+
boolean: boolean,
|
|
1228
1259
|
float32: float32,
|
|
1229
1260
|
float64: float64,
|
|
1261
|
+
int16: int16,
|
|
1262
|
+
int32: int32,
|
|
1230
1263
|
int64: int64,
|
|
1231
|
-
|
|
1264
|
+
int8: int8,
|
|
1265
|
+
number: number,
|
|
1266
|
+
numberCheck: numberCheck,
|
|
1232
1267
|
readFloat32: readFloat32,
|
|
1233
1268
|
readFloat64: readFloat64,
|
|
1234
|
-
boolean: boolean,
|
|
1235
1269
|
string: string,
|
|
1236
1270
|
stringCheck: stringCheck,
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1271
|
+
switchStructureCheck: switchStructureCheck,
|
|
1272
|
+
uint16: uint16,
|
|
1273
|
+
uint32: uint32,
|
|
1274
|
+
uint64: uint64,
|
|
1275
|
+
uint8: uint8,
|
|
1276
|
+
utf8Read: utf8Read
|
|
1241
1277
|
});
|
|
1242
1278
|
|
|
1243
1279
|
const DEFINITION_MISMATCH = -1;
|
|
1244
1280
|
function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
|
|
1245
|
-
const $root = decoder
|
|
1281
|
+
const $root = decoder.root;
|
|
1246
1282
|
const previousValue = ref[$getByIndex](index);
|
|
1247
1283
|
let value;
|
|
1248
1284
|
if ((operation & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
@@ -1341,18 +1377,19 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1341
1377
|
}
|
|
1342
1378
|
const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
1343
1379
|
const first_byte = bytes[it.offset++];
|
|
1344
|
-
const metadata = ref
|
|
1380
|
+
const metadata = ref.constructor[Symbol.metadata];
|
|
1345
1381
|
// "compressed" index + operation
|
|
1346
1382
|
const operation = (first_byte >> 6) << 6;
|
|
1347
1383
|
const index = first_byte % (operation || 255);
|
|
1348
1384
|
// skip early if field is not defined
|
|
1349
1385
|
const field = metadata[index];
|
|
1350
1386
|
if (field === undefined) {
|
|
1387
|
+
console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
|
|
1351
1388
|
return DEFINITION_MISMATCH;
|
|
1352
1389
|
}
|
|
1353
|
-
const { value, previousValue } = decodeValue(decoder, operation, ref, index,
|
|
1390
|
+
const { value, previousValue } = decodeValue(decoder, operation, ref, index, field.type, bytes, it, allChanges);
|
|
1354
1391
|
if (value !== null && value !== undefined) {
|
|
1355
|
-
ref[field] = value;
|
|
1392
|
+
ref[field.name] = value;
|
|
1356
1393
|
}
|
|
1357
1394
|
// add change
|
|
1358
1395
|
if (previousValue !== value) {
|
|
@@ -1360,7 +1397,7 @@ const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1360
1397
|
ref,
|
|
1361
1398
|
refId: decoder.currentRefId,
|
|
1362
1399
|
op: operation,
|
|
1363
|
-
field: field,
|
|
1400
|
+
field: field.name,
|
|
1364
1401
|
value,
|
|
1365
1402
|
previousValue,
|
|
1366
1403
|
});
|
|
@@ -1428,7 +1465,8 @@ const decodeKeyValueOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1428
1465
|
};
|
|
1429
1466
|
const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
1430
1467
|
// "uncompressed" index + operation (array/map items)
|
|
1431
|
-
|
|
1468
|
+
let operation = bytes[it.offset++];
|
|
1469
|
+
let index;
|
|
1432
1470
|
if (operation === OPERATION.CLEAR) {
|
|
1433
1471
|
//
|
|
1434
1472
|
// When decoding:
|
|
@@ -1442,8 +1480,8 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1442
1480
|
else if (operation === OPERATION.DELETE_BY_REFID) {
|
|
1443
1481
|
// TODO: refactor here, try to follow same flow as below
|
|
1444
1482
|
const refId = number(bytes, it);
|
|
1445
|
-
const previousValue = decoder
|
|
1446
|
-
|
|
1483
|
+
const previousValue = decoder.root.refs.get(refId);
|
|
1484
|
+
index = ref.findIndex((value) => value === previousValue);
|
|
1447
1485
|
ref[$deleteByIndex](index);
|
|
1448
1486
|
allChanges.push({
|
|
1449
1487
|
ref,
|
|
@@ -1456,7 +1494,17 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1456
1494
|
});
|
|
1457
1495
|
return;
|
|
1458
1496
|
}
|
|
1459
|
-
|
|
1497
|
+
else if (operation === OPERATION.ADD_BY_REFID) {
|
|
1498
|
+
const refId = number(bytes, it);
|
|
1499
|
+
const itemByRefId = decoder.root.refs.get(refId);
|
|
1500
|
+
// use existing index, or push new value
|
|
1501
|
+
index = (itemByRefId)
|
|
1502
|
+
? ref.findIndex((value) => value === itemByRefId)
|
|
1503
|
+
: ref.length;
|
|
1504
|
+
}
|
|
1505
|
+
else {
|
|
1506
|
+
index = number(bytes, it);
|
|
1507
|
+
}
|
|
1460
1508
|
const type = ref[$childType];
|
|
1461
1509
|
let dynamicIndex = index;
|
|
1462
1510
|
const { value, previousValue } = decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges);
|
|
@@ -1480,6 +1528,47 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1480
1528
|
}
|
|
1481
1529
|
};
|
|
1482
1530
|
|
|
1531
|
+
class EncodeSchemaError extends Error {
|
|
1532
|
+
}
|
|
1533
|
+
function assertType(value, type, klass, field) {
|
|
1534
|
+
let typeofTarget;
|
|
1535
|
+
let allowNull = false;
|
|
1536
|
+
switch (type) {
|
|
1537
|
+
case "number":
|
|
1538
|
+
case "int8":
|
|
1539
|
+
case "uint8":
|
|
1540
|
+
case "int16":
|
|
1541
|
+
case "uint16":
|
|
1542
|
+
case "int32":
|
|
1543
|
+
case "uint32":
|
|
1544
|
+
case "int64":
|
|
1545
|
+
case "uint64":
|
|
1546
|
+
case "float32":
|
|
1547
|
+
case "float64":
|
|
1548
|
+
typeofTarget = "number";
|
|
1549
|
+
if (isNaN(value)) {
|
|
1550
|
+
console.log(`trying to encode "NaN" in ${klass.constructor.name}#${field}`);
|
|
1551
|
+
}
|
|
1552
|
+
break;
|
|
1553
|
+
case "string":
|
|
1554
|
+
typeofTarget = "string";
|
|
1555
|
+
allowNull = true;
|
|
1556
|
+
break;
|
|
1557
|
+
case "boolean":
|
|
1558
|
+
// boolean is always encoded as true/false based on truthiness
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) {
|
|
1562
|
+
let foundValue = `'${JSON.stringify(value)}'${(value && value.constructor && ` (${value.constructor.name})`) || ''}`;
|
|
1563
|
+
throw new EncodeSchemaError(`a '${typeofTarget}' was expected, but ${foundValue} was provided in ${klass.constructor.name}#${field}`);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
function assertInstanceType(value, type, instance, field) {
|
|
1567
|
+
if (!(value instanceof type)) {
|
|
1568
|
+
throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && value.constructor.name}' was provided in ${instance.constructor.name}#${field}`);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1483
1572
|
var _a$4, _b$4;
|
|
1484
1573
|
const DEFAULT_SORT = (a, b) => {
|
|
1485
1574
|
const A = a.toString();
|
|
@@ -1545,6 +1634,7 @@ class ArraySchema {
|
|
|
1545
1634
|
}
|
|
1546
1635
|
else {
|
|
1547
1636
|
if (setValue[$changes]) {
|
|
1637
|
+
assertInstanceType(setValue, obj[$childType], obj, key);
|
|
1548
1638
|
if (obj.items[key] !== undefined) {
|
|
1549
1639
|
if (setValue[$changes][$isNew]) {
|
|
1550
1640
|
this[$changes].indexedOperation(Number(key), OPERATION.MOVE_AND_ADD);
|
|
@@ -1591,6 +1681,7 @@ class ArraySchema {
|
|
|
1591
1681
|
}
|
|
1592
1682
|
});
|
|
1593
1683
|
this[$changes] = new ChangeTree(proxy);
|
|
1684
|
+
this[$changes].indexes = {};
|
|
1594
1685
|
this.push.apply(this, items);
|
|
1595
1686
|
return proxy;
|
|
1596
1687
|
}
|
|
@@ -1615,9 +1706,11 @@ class ArraySchema {
|
|
|
1615
1706
|
if (value === undefined || value === null) {
|
|
1616
1707
|
return;
|
|
1617
1708
|
}
|
|
1709
|
+
else if (typeof (value) === "object" && this[$childType]) {
|
|
1710
|
+
assertInstanceType(value, this[$childType], this, i);
|
|
1711
|
+
}
|
|
1618
1712
|
const changeTree = this[$changes];
|
|
1619
1713
|
changeTree.indexedOperation(length, OPERATION.ADD, this.items.length);
|
|
1620
|
-
// changeTree.indexes[length] = length;
|
|
1621
1714
|
this.items.push(value);
|
|
1622
1715
|
this.tmpItems.push(value);
|
|
1623
1716
|
//
|
|
@@ -1865,14 +1958,6 @@ class ArraySchema {
|
|
|
1865
1958
|
lastIndexOf(searchElement, fromIndex = this.length - 1) {
|
|
1866
1959
|
return this.items.lastIndexOf(searchElement, fromIndex);
|
|
1867
1960
|
}
|
|
1868
|
-
/**
|
|
1869
|
-
* Determines whether all the members of an array satisfy the specified test.
|
|
1870
|
-
* @param callbackfn A function that accepts up to three arguments. The every method calls
|
|
1871
|
-
* the callbackfn function for each element in the array until the callbackfn returns a value
|
|
1872
|
-
* which is coercible to the Boolean value false, or until the end of the array.
|
|
1873
|
-
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
|
|
1874
|
-
* If thisArg is omitted, undefined is used as the this value.
|
|
1875
|
-
*/
|
|
1876
1961
|
every(callbackfn, thisArg) {
|
|
1877
1962
|
return this.items.every(callbackfn, thisArg);
|
|
1878
1963
|
}
|
|
@@ -2151,6 +2236,7 @@ class MapSchema {
|
|
|
2151
2236
|
this.$items = new Map();
|
|
2152
2237
|
this.$indexes = new Map();
|
|
2153
2238
|
this[$changes] = new ChangeTree(this);
|
|
2239
|
+
this[$changes].indexes = {};
|
|
2154
2240
|
if (initialValues) {
|
|
2155
2241
|
if (initialValues instanceof Map ||
|
|
2156
2242
|
initialValues instanceof MapSchema) {
|
|
@@ -2177,6 +2263,9 @@ class MapSchema {
|
|
|
2177
2263
|
if (value === undefined || value === null) {
|
|
2178
2264
|
throw new Error(`MapSchema#set('${key}', ${value}): trying to set ${value} value on '${key}'.`);
|
|
2179
2265
|
}
|
|
2266
|
+
else if (typeof (value) === "object" && this[$childType]) {
|
|
2267
|
+
assertInstanceType(value, this[$childType], this, key);
|
|
2268
|
+
}
|
|
2180
2269
|
// Force "key" as string
|
|
2181
2270
|
// See: https://github.com/colyseus/colyseus/issues/561#issuecomment-1646733468
|
|
2182
2271
|
key = key.toString();
|
|
@@ -2316,7 +2405,6 @@ class MapSchema {
|
|
|
2316
2405
|
}
|
|
2317
2406
|
registerType("map", { constructor: MapSchema });
|
|
2318
2407
|
|
|
2319
|
-
const DEFAULT_VIEW_TAG = -1;
|
|
2320
2408
|
class TypeContext {
|
|
2321
2409
|
/**
|
|
2322
2410
|
* For inheritance support
|
|
@@ -2338,6 +2426,7 @@ class TypeContext {
|
|
|
2338
2426
|
this.types = {};
|
|
2339
2427
|
this.schemas = new Map();
|
|
2340
2428
|
this.hasFilters = false;
|
|
2429
|
+
this.parentFiltered = {};
|
|
2341
2430
|
if (rootClass) {
|
|
2342
2431
|
this.discoverTypes(rootClass);
|
|
2343
2432
|
}
|
|
@@ -2354,44 +2443,51 @@ class TypeContext {
|
|
|
2354
2443
|
return false;
|
|
2355
2444
|
}
|
|
2356
2445
|
this.types[typeid] = schema;
|
|
2446
|
+
//
|
|
2447
|
+
// Workaround to allow using an empty Schema (with no `@type()` fields)
|
|
2448
|
+
//
|
|
2449
|
+
if (schema[Symbol.metadata] === undefined) {
|
|
2450
|
+
Metadata.init(schema);
|
|
2451
|
+
}
|
|
2357
2452
|
this.schemas.set(schema, typeid);
|
|
2358
2453
|
return true;
|
|
2359
2454
|
}
|
|
2360
2455
|
getTypeId(klass) {
|
|
2361
2456
|
return this.schemas.get(klass);
|
|
2362
2457
|
}
|
|
2363
|
-
discoverTypes(klass) {
|
|
2458
|
+
discoverTypes(klass, parentIndex, parentFieldViewTag) {
|
|
2364
2459
|
if (!this.add(klass)) {
|
|
2365
2460
|
return;
|
|
2366
2461
|
}
|
|
2367
2462
|
// add classes inherited from this base class
|
|
2368
2463
|
TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
|
|
2369
|
-
this.discoverTypes(child);
|
|
2464
|
+
this.discoverTypes(child, parentIndex, parentFieldViewTag);
|
|
2370
2465
|
});
|
|
2371
|
-
|
|
2372
|
-
if (klass[Symbol.metadata] === undefined) {
|
|
2373
|
-
klass[Symbol.metadata] = {};
|
|
2374
|
-
}
|
|
2375
|
-
// const metadata = Metadata.getFor(klass);
|
|
2376
|
-
const metadata = klass[Symbol.metadata];
|
|
2466
|
+
const metadata = (klass[Symbol.metadata] ??= {});
|
|
2377
2467
|
// if any schema/field has filters, mark "context" as having filters.
|
|
2378
2468
|
if (metadata[-2]) {
|
|
2379
2469
|
this.hasFilters = true;
|
|
2380
2470
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2471
|
+
if (parentFieldViewTag !== undefined) {
|
|
2472
|
+
this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
|
|
2473
|
+
}
|
|
2474
|
+
for (const fieldIndex in metadata) {
|
|
2475
|
+
const index = fieldIndex;
|
|
2476
|
+
const fieldType = metadata[index].type;
|
|
2477
|
+
const viewTag = metadata[index].tag;
|
|
2383
2478
|
if (typeof (fieldType) === "string") {
|
|
2384
2479
|
continue;
|
|
2385
2480
|
}
|
|
2386
2481
|
if (Array.isArray(fieldType)) {
|
|
2387
2482
|
const type = fieldType[0];
|
|
2483
|
+
// skip primitive types
|
|
2388
2484
|
if (type === "string") {
|
|
2389
2485
|
continue;
|
|
2390
2486
|
}
|
|
2391
|
-
this.discoverTypes(type);
|
|
2487
|
+
this.discoverTypes(type, index, viewTag);
|
|
2392
2488
|
}
|
|
2393
2489
|
else if (typeof (fieldType) === "function") {
|
|
2394
|
-
this.discoverTypes(fieldType);
|
|
2490
|
+
this.discoverTypes(fieldType, viewTag);
|
|
2395
2491
|
}
|
|
2396
2492
|
else {
|
|
2397
2493
|
const type = Object.values(fieldType)[0];
|
|
@@ -2399,11 +2495,13 @@ class TypeContext {
|
|
|
2399
2495
|
if (typeof (type) === "string") {
|
|
2400
2496
|
continue;
|
|
2401
2497
|
}
|
|
2402
|
-
this.discoverTypes(type);
|
|
2498
|
+
this.discoverTypes(type, index, viewTag);
|
|
2403
2499
|
}
|
|
2404
2500
|
}
|
|
2405
2501
|
}
|
|
2406
2502
|
}
|
|
2503
|
+
|
|
2504
|
+
const DEFAULT_VIEW_TAG = -1;
|
|
2407
2505
|
/**
|
|
2408
2506
|
* [See documentation](https://docs.colyseus.io/state/schema/)
|
|
2409
2507
|
*
|
|
@@ -2551,18 +2649,20 @@ function view(tag = DEFAULT_VIEW_TAG) {
|
|
|
2551
2649
|
const constructor = target.constructor;
|
|
2552
2650
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2553
2651
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2652
|
+
// TODO: use Metadata.initialize()
|
|
2554
2653
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
}
|
|
2654
|
+
// const fieldIndex = metadata[fieldName];
|
|
2655
|
+
// if (!metadata[fieldIndex]) {
|
|
2656
|
+
// //
|
|
2657
|
+
// // detect index for this field, considering inheritance
|
|
2658
|
+
// //
|
|
2659
|
+
// metadata[fieldIndex] = {
|
|
2660
|
+
// type: undefined,
|
|
2661
|
+
// index: (metadata[-1] // current structure already has fields defined
|
|
2662
|
+
// ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
|
|
2663
|
+
// ?? -1) + 1 // no fields defined
|
|
2664
|
+
// }
|
|
2665
|
+
// }
|
|
2566
2666
|
Metadata.setTag(metadata, fieldName, tag);
|
|
2567
2667
|
};
|
|
2568
2668
|
}
|
|
@@ -2575,18 +2675,18 @@ function type(type, options) {
|
|
|
2575
2675
|
// for inheritance support
|
|
2576
2676
|
TypeContext.register(constructor);
|
|
2577
2677
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2578
|
-
const parentMetadata = parentClass[Symbol.metadata];
|
|
2579
|
-
const metadata =
|
|
2580
|
-
let fieldIndex;
|
|
2678
|
+
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
2679
|
+
const metadata = Metadata.initialize(constructor, parentMetadata);
|
|
2680
|
+
let fieldIndex = metadata[field];
|
|
2581
2681
|
/**
|
|
2582
2682
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
2583
2683
|
*/
|
|
2584
|
-
if (metadata[
|
|
2585
|
-
if (metadata[
|
|
2684
|
+
if (metadata[fieldIndex]) {
|
|
2685
|
+
if (metadata[fieldIndex].deprecated) {
|
|
2586
2686
|
// do not create accessors for deprecated properties.
|
|
2587
2687
|
return;
|
|
2588
2688
|
}
|
|
2589
|
-
else if (metadata[
|
|
2689
|
+
else if (metadata[fieldIndex].type !== undefined) {
|
|
2590
2690
|
// trying to define same property multiple times across inheritance.
|
|
2591
2691
|
// https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
|
|
2592
2692
|
try {
|
|
@@ -2597,9 +2697,6 @@ function type(type, options) {
|
|
|
2597
2697
|
throw new Error(`${e.message} ${definitionAtLine}`);
|
|
2598
2698
|
}
|
|
2599
2699
|
}
|
|
2600
|
-
else {
|
|
2601
|
-
fieldIndex = metadata[field].index;
|
|
2602
|
-
}
|
|
2603
2700
|
}
|
|
2604
2701
|
else {
|
|
2605
2702
|
//
|
|
@@ -2625,11 +2722,11 @@ function type(type, options) {
|
|
|
2625
2722
|
const childType = (complexTypeKlass)
|
|
2626
2723
|
? Object.values(type)[0]
|
|
2627
2724
|
: type;
|
|
2628
|
-
Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass
|
|
2725
|
+
Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass));
|
|
2629
2726
|
}
|
|
2630
2727
|
};
|
|
2631
2728
|
}
|
|
2632
|
-
function getPropertyDescriptor(fieldCached, fieldIndex, type, complexTypeKlass
|
|
2729
|
+
function getPropertyDescriptor(fieldCached, fieldIndex, type, complexTypeKlass) {
|
|
2633
2730
|
return {
|
|
2634
2731
|
get: function () { return this[fieldCached]; },
|
|
2635
2732
|
set: function (value) {
|
|
@@ -2651,22 +2748,27 @@ function getPropertyDescriptor(fieldCached, fieldIndex, type, complexTypeKlass,
|
|
|
2651
2748
|
}
|
|
2652
2749
|
value[$childType] = type;
|
|
2653
2750
|
}
|
|
2751
|
+
else if (typeof (type) !== "string") {
|
|
2752
|
+
assertInstanceType(value, type, this, fieldCached.substring(1));
|
|
2753
|
+
}
|
|
2754
|
+
else {
|
|
2755
|
+
assertType(value, type, this, fieldCached.substring(1));
|
|
2756
|
+
}
|
|
2757
|
+
const changeTree = this[$changes];
|
|
2654
2758
|
//
|
|
2655
2759
|
// Replacing existing "ref", remove it from root.
|
|
2656
2760
|
// TODO: if there are other references to this instance, we should not remove it from root.
|
|
2657
2761
|
//
|
|
2658
2762
|
if (previousValue !== undefined && previousValue[$changes]) {
|
|
2659
|
-
|
|
2763
|
+
changeTree.root?.remove(previousValue[$changes]);
|
|
2660
2764
|
}
|
|
2661
2765
|
// flag the change for encoding.
|
|
2662
|
-
this.constructor[$track](
|
|
2766
|
+
this.constructor[$track](changeTree, fieldIndex, OPERATION.ADD);
|
|
2663
2767
|
//
|
|
2664
2768
|
// call setParent() recursively for this and its child
|
|
2665
2769
|
// structures.
|
|
2666
2770
|
//
|
|
2667
|
-
|
|
2668
|
-
value[$changes].setParent(this, this[$changes].root, metadata[field].index);
|
|
2669
|
-
}
|
|
2771
|
+
value[$changes]?.setParent(this, changeTree.root, fieldIndex);
|
|
2670
2772
|
}
|
|
2671
2773
|
else if (previousValue !== undefined) {
|
|
2672
2774
|
//
|
|
@@ -2693,20 +2795,22 @@ function deprecated(throws = true) {
|
|
|
2693
2795
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2694
2796
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2695
2797
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2798
|
+
const fieldIndex = metadata[field];
|
|
2799
|
+
// if (!metadata[field]) {
|
|
2800
|
+
// //
|
|
2801
|
+
// // detect index for this field, considering inheritance
|
|
2802
|
+
// //
|
|
2803
|
+
// metadata[field] = {
|
|
2804
|
+
// type: undefined,
|
|
2805
|
+
// index: (metadata[-1] // current structure already has fields defined
|
|
2806
|
+
// ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
|
|
2807
|
+
// ?? -1) + 1 // no fields defined
|
|
2808
|
+
// }
|
|
2809
|
+
// }
|
|
2810
|
+
metadata[fieldIndex].deprecated = true;
|
|
2708
2811
|
if (throws) {
|
|
2709
|
-
metadata[
|
|
2812
|
+
metadata[$descriptors] ??= {};
|
|
2813
|
+
metadata[$descriptors][field] = {
|
|
2710
2814
|
get: function () { throw new Error(`${field} is deprecated.`); },
|
|
2711
2815
|
set: function (value) { },
|
|
2712
2816
|
enumerable: false,
|
|
@@ -2714,8 +2818,8 @@ function deprecated(throws = true) {
|
|
|
2714
2818
|
};
|
|
2715
2819
|
}
|
|
2716
2820
|
// flag metadata[field] as non-enumerable
|
|
2717
|
-
Object.defineProperty(metadata,
|
|
2718
|
-
value: metadata[
|
|
2821
|
+
Object.defineProperty(metadata, fieldIndex, {
|
|
2822
|
+
value: metadata[fieldIndex],
|
|
2719
2823
|
enumerable: false,
|
|
2720
2824
|
configurable: true
|
|
2721
2825
|
});
|
|
@@ -2781,35 +2885,7 @@ class Schema {
|
|
|
2781
2885
|
enumerable: false,
|
|
2782
2886
|
writable: true
|
|
2783
2887
|
});
|
|
2784
|
-
|
|
2785
|
-
// Define property descriptors
|
|
2786
|
-
for (const field in metadata) {
|
|
2787
|
-
if (metadata[field].descriptor) {
|
|
2788
|
-
// for encoder
|
|
2789
|
-
Object.defineProperty(instance, `_${field}`, {
|
|
2790
|
-
value: undefined,
|
|
2791
|
-
writable: true,
|
|
2792
|
-
enumerable: false,
|
|
2793
|
-
configurable: true,
|
|
2794
|
-
});
|
|
2795
|
-
Object.defineProperty(instance, field, metadata[field].descriptor);
|
|
2796
|
-
}
|
|
2797
|
-
else {
|
|
2798
|
-
// for decoder
|
|
2799
|
-
Object.defineProperty(instance, field, {
|
|
2800
|
-
value: undefined,
|
|
2801
|
-
writable: true,
|
|
2802
|
-
enumerable: true,
|
|
2803
|
-
configurable: true,
|
|
2804
|
-
});
|
|
2805
|
-
}
|
|
2806
|
-
// Object.defineProperty(instance, field, {
|
|
2807
|
-
// ...instance.constructor[Symbol.metadata][field].descriptor
|
|
2808
|
-
// });
|
|
2809
|
-
// if (args[0]?.hasOwnProperty(field)) {
|
|
2810
|
-
// instance[field] = args[0][field];
|
|
2811
|
-
// }
|
|
2812
|
-
}
|
|
2888
|
+
Object.defineProperties(instance, instance.constructor[Symbol.metadata]?.[$descriptors] || {});
|
|
2813
2889
|
}
|
|
2814
2890
|
static is(type) {
|
|
2815
2891
|
return typeof (type[Symbol.metadata]) === "object";
|
|
@@ -2833,7 +2909,7 @@ class Schema {
|
|
|
2833
2909
|
*/
|
|
2834
2910
|
static [$filter](ref, index, view) {
|
|
2835
2911
|
const metadata = ref.constructor[Symbol.metadata];
|
|
2836
|
-
const tag = metadata[
|
|
2912
|
+
const tag = metadata[index]?.tag;
|
|
2837
2913
|
if (view === undefined) {
|
|
2838
2914
|
// shared pass/encode: encode if doesn't have a tag
|
|
2839
2915
|
return tag === undefined;
|
|
@@ -2854,12 +2930,21 @@ class Schema {
|
|
|
2854
2930
|
}
|
|
2855
2931
|
// allow inherited classes to have a constructor
|
|
2856
2932
|
constructor(...args) {
|
|
2857
|
-
|
|
2933
|
+
//
|
|
2934
|
+
// inline
|
|
2935
|
+
// Schema.initialize(this);
|
|
2936
|
+
//
|
|
2937
|
+
Object.defineProperty(this, $changes, {
|
|
2938
|
+
value: new ChangeTree(this),
|
|
2939
|
+
enumerable: false,
|
|
2940
|
+
writable: true
|
|
2941
|
+
});
|
|
2942
|
+
Object.defineProperties(this, this.constructor[Symbol.metadata]?.[$descriptors] || {});
|
|
2858
2943
|
//
|
|
2859
2944
|
// Assign initial values
|
|
2860
2945
|
//
|
|
2861
2946
|
if (args[0]) {
|
|
2862
|
-
|
|
2947
|
+
Object.assign(this, args[0]);
|
|
2863
2948
|
}
|
|
2864
2949
|
}
|
|
2865
2950
|
assign(props) {
|
|
@@ -2873,7 +2958,8 @@ class Schema {
|
|
|
2873
2958
|
* @param operation OPERATION to perform (detected automatically)
|
|
2874
2959
|
*/
|
|
2875
2960
|
setDirty(property, operation) {
|
|
2876
|
-
this
|
|
2961
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
2962
|
+
this[$changes].change(metadata[metadata[property]].index, operation);
|
|
2877
2963
|
}
|
|
2878
2964
|
clone() {
|
|
2879
2965
|
const cloned = new (this.constructor);
|
|
@@ -2882,7 +2968,9 @@ class Schema {
|
|
|
2882
2968
|
// TODO: clone all properties, not only annotated ones
|
|
2883
2969
|
//
|
|
2884
2970
|
// for (const field in this) {
|
|
2885
|
-
for (const
|
|
2971
|
+
for (const fieldIndex in metadata) {
|
|
2972
|
+
// const field = metadata[metadata[fieldIndex]].name;
|
|
2973
|
+
const field = metadata[fieldIndex].name;
|
|
2886
2974
|
if (typeof (this[field]) === "object" &&
|
|
2887
2975
|
typeof (this[field]?.clone) === "function") {
|
|
2888
2976
|
// deep clone
|
|
@@ -2896,10 +2984,11 @@ class Schema {
|
|
|
2896
2984
|
return cloned;
|
|
2897
2985
|
}
|
|
2898
2986
|
toJSON() {
|
|
2899
|
-
const metadata = this.constructor[Symbol.metadata];
|
|
2900
2987
|
const obj = {};
|
|
2901
|
-
|
|
2902
|
-
|
|
2988
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
2989
|
+
for (const index in metadata) {
|
|
2990
|
+
const field = metadata[index];
|
|
2991
|
+
const fieldName = field.name;
|
|
2903
2992
|
if (!field.deprecated && this[fieldName] !== null && typeof (this[fieldName]) !== "undefined") {
|
|
2904
2993
|
obj[fieldName] = (typeof (this[fieldName]['toJSON']) === "function")
|
|
2905
2994
|
? this[fieldName]['toJSON']()
|
|
@@ -2912,10 +3001,12 @@ class Schema {
|
|
|
2912
3001
|
this[$changes].discardAll();
|
|
2913
3002
|
}
|
|
2914
3003
|
[$getByIndex](index) {
|
|
2915
|
-
|
|
3004
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
3005
|
+
return this[metadata[index].name];
|
|
2916
3006
|
}
|
|
2917
3007
|
[$deleteByIndex](index) {
|
|
2918
|
-
this
|
|
3008
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
3009
|
+
this[metadata[index].name] = undefined;
|
|
2919
3010
|
}
|
|
2920
3011
|
static debugRefIds(instance, jsonContents = true, level = 0) {
|
|
2921
3012
|
const ref = instance;
|
|
@@ -2957,13 +3048,13 @@ class Schema {
|
|
|
2957
3048
|
}
|
|
2958
3049
|
return output;
|
|
2959
3050
|
}
|
|
2960
|
-
static debugChangesDeep(ref) {
|
|
3051
|
+
static debugChangesDeep(ref, changeSetName = "changes") {
|
|
2961
3052
|
let output = "";
|
|
2962
3053
|
const rootChangeTree = ref[$changes];
|
|
2963
3054
|
const changeTrees = new Map();
|
|
2964
3055
|
let totalInstances = 0;
|
|
2965
3056
|
let totalOperations = 0;
|
|
2966
|
-
for (const [changeTree, changes] of (rootChangeTree.root.
|
|
3057
|
+
for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
|
|
2967
3058
|
let includeChangeTree = false;
|
|
2968
3059
|
let parentChangeTrees = [];
|
|
2969
3060
|
let parentChangeTree = changeTree.parent?.[$changes];
|
|
@@ -3039,6 +3130,7 @@ class CollectionSchema {
|
|
|
3039
3130
|
this.$indexes = new Map();
|
|
3040
3131
|
this.$refId = 0;
|
|
3041
3132
|
this[$changes] = new ChangeTree(this);
|
|
3133
|
+
this[$changes].indexes = {};
|
|
3042
3134
|
if (initialValues) {
|
|
3043
3135
|
initialValues.forEach((v) => this.add(v));
|
|
3044
3136
|
}
|
|
@@ -3194,6 +3286,7 @@ class SetSchema {
|
|
|
3194
3286
|
this.$indexes = new Map();
|
|
3195
3287
|
this.$refId = 0;
|
|
3196
3288
|
this[$changes] = new ChangeTree(this);
|
|
3289
|
+
this[$changes].indexes = {};
|
|
3197
3290
|
if (initialValues) {
|
|
3198
3291
|
initialValues.forEach((v) => this.add(v));
|
|
3199
3292
|
}
|
|
@@ -3349,6 +3442,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
3349
3442
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
3350
3443
|
PERFORMANCE OF THIS SOFTWARE.
|
|
3351
3444
|
***************************************************************************** */
|
|
3445
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
3446
|
+
|
|
3352
3447
|
|
|
3353
3448
|
function __decorate(decorators, target, key, desc) {
|
|
3354
3449
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3362,37 +3457,82 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
3362
3457
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
3363
3458
|
};
|
|
3364
3459
|
|
|
3460
|
+
class Root {
|
|
3461
|
+
constructor(types) {
|
|
3462
|
+
this.types = types;
|
|
3463
|
+
this.nextUniqueId = 0;
|
|
3464
|
+
this.refCount = new WeakMap();
|
|
3465
|
+
// all changes
|
|
3466
|
+
this.allChanges = new Map();
|
|
3467
|
+
this.allFilteredChanges = new Map();
|
|
3468
|
+
// pending changes to be encoded
|
|
3469
|
+
this.changes = new Map();
|
|
3470
|
+
this.filteredChanges = new Map();
|
|
3471
|
+
}
|
|
3472
|
+
getNextUniqueId() {
|
|
3473
|
+
return this.nextUniqueId++;
|
|
3474
|
+
}
|
|
3475
|
+
add(changeTree) {
|
|
3476
|
+
const refCount = this.refCount.get(changeTree) || 0;
|
|
3477
|
+
this.refCount.set(changeTree, refCount + 1);
|
|
3478
|
+
}
|
|
3479
|
+
remove(changeTree) {
|
|
3480
|
+
const refCount = this.refCount.get(changeTree);
|
|
3481
|
+
if (refCount <= 1) {
|
|
3482
|
+
this.allChanges.delete(changeTree);
|
|
3483
|
+
this.changes.delete(changeTree);
|
|
3484
|
+
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
3485
|
+
this.allFilteredChanges.delete(changeTree);
|
|
3486
|
+
this.filteredChanges.delete(changeTree);
|
|
3487
|
+
}
|
|
3488
|
+
this.refCount.delete(changeTree);
|
|
3489
|
+
}
|
|
3490
|
+
else {
|
|
3491
|
+
this.refCount.set(changeTree, refCount - 1);
|
|
3492
|
+
}
|
|
3493
|
+
changeTree.forEachChild((child, _) => this.remove(child));
|
|
3494
|
+
}
|
|
3495
|
+
clear() {
|
|
3496
|
+
this.changes.clear();
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3365
3500
|
class Encoder {
|
|
3366
3501
|
static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
|
|
3367
|
-
constructor(
|
|
3502
|
+
constructor(state) {
|
|
3368
3503
|
this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
|
|
3369
|
-
this.setRoot(root);
|
|
3370
3504
|
//
|
|
3371
3505
|
// TODO: cache and restore "Context" based on root schema
|
|
3372
3506
|
// (to avoid creating a new context for every new room)
|
|
3373
3507
|
//
|
|
3374
|
-
this.context = new TypeContext(
|
|
3508
|
+
this.context = new TypeContext(state.constructor);
|
|
3509
|
+
this.root = new Root(this.context);
|
|
3510
|
+
this.setState(state);
|
|
3375
3511
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
3376
3512
|
// this.context.schemas.forEach((id, schema) => {
|
|
3377
3513
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3378
3514
|
// });
|
|
3379
3515
|
}
|
|
3380
|
-
|
|
3381
|
-
this.root = new Root();
|
|
3516
|
+
setState(state) {
|
|
3382
3517
|
this.state = state;
|
|
3383
|
-
state[$changes].setRoot(this.root);
|
|
3518
|
+
this.state[$changes].setRoot(this.root);
|
|
3384
3519
|
}
|
|
3385
|
-
encode(it = { offset: 0 }, view,
|
|
3386
|
-
|
|
3387
|
-
const isEncodeAll = this.root.allChanges === changeTrees;
|
|
3520
|
+
encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes, isEncodeAll = this.root.allChanges === changeTrees, initialOffset = it.offset // cache current offset in case we need to resize the buffer
|
|
3521
|
+
) {
|
|
3388
3522
|
const hasView = (view !== undefined);
|
|
3389
3523
|
const rootChangeTree = this.state[$changes];
|
|
3390
|
-
const
|
|
3391
|
-
for (const [changeTree, changes] of
|
|
3524
|
+
const shouldClearChanges = !isEncodeAll && !hasView;
|
|
3525
|
+
for (const [changeTree, changes] of changeTrees.entries()) {
|
|
3392
3526
|
const ref = changeTree.ref;
|
|
3393
3527
|
const ctor = ref['constructor'];
|
|
3394
3528
|
const encoder = ctor[$encoder];
|
|
3395
3529
|
const filter = ctor[$filter];
|
|
3530
|
+
// try { throw new Error(); } catch (e) {
|
|
3531
|
+
// // only print if not coming from Reflection.ts
|
|
3532
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3533
|
+
// console.log("ChangeTree:", { ref: ref.constructor.name, });
|
|
3534
|
+
// }
|
|
3535
|
+
// }
|
|
3396
3536
|
if (hasView) {
|
|
3397
3537
|
if (!view.items.has(changeTree)) {
|
|
3398
3538
|
view.invisible.add(changeTree);
|
|
@@ -3403,9 +3543,10 @@ class Encoder {
|
|
|
3403
3543
|
}
|
|
3404
3544
|
}
|
|
3405
3545
|
// skip root `refId` if it's the first change tree
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3546
|
+
// (unless it "hasView", which will need to revisit the root)
|
|
3547
|
+
if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
|
|
3548
|
+
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3549
|
+
number$1(buffer, changeTree.refId, it);
|
|
3409
3550
|
}
|
|
3410
3551
|
const changesIterator = changes.entries();
|
|
3411
3552
|
for (const [fieldIndex, operation] of changesIterator) {
|
|
@@ -3417,74 +3558,94 @@ class Encoder {
|
|
|
3417
3558
|
// TODO: avoid checking if no view tags were defined
|
|
3418
3559
|
//
|
|
3419
3560
|
if (filter && !filter(ref, fieldIndex, view)) {
|
|
3420
|
-
// console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
|
|
3421
3561
|
// console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
|
|
3422
3562
|
// view?.invisible.add(changeTree);
|
|
3423
3563
|
continue;
|
|
3424
3564
|
}
|
|
3425
|
-
//
|
|
3426
|
-
//
|
|
3427
|
-
//
|
|
3428
|
-
//
|
|
3429
|
-
//
|
|
3430
|
-
|
|
3565
|
+
// try { throw new Error(); } catch (e) {
|
|
3566
|
+
// // only print if not coming from Reflection.ts
|
|
3567
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
3568
|
+
// console.log("WILL ENCODE", {
|
|
3569
|
+
// ref: changeTree.ref.constructor.name,
|
|
3570
|
+
// fieldIndex,
|
|
3571
|
+
// operation: OPERATION[operation],
|
|
3572
|
+
// });
|
|
3573
|
+
// }
|
|
3574
|
+
// }
|
|
3575
|
+
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
3431
3576
|
}
|
|
3577
|
+
// if (shouldClearChanges) {
|
|
3578
|
+
// changeTree.endEncode();
|
|
3579
|
+
// }
|
|
3432
3580
|
}
|
|
3433
|
-
if (it.offset >
|
|
3434
|
-
const newSize = getNextPowerOf2(
|
|
3435
|
-
console.warn(
|
|
3581
|
+
if (it.offset > buffer.byteLength) {
|
|
3582
|
+
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
3583
|
+
console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
|
|
3584
|
+
|
|
3585
|
+
import { Encoder } from "@colyseus/schema";
|
|
3586
|
+
Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
|
|
3587
|
+
`);
|
|
3436
3588
|
//
|
|
3437
3589
|
// resize buffer and re-encode (TODO: can we avoid re-encoding here?)
|
|
3438
3590
|
//
|
|
3439
|
-
|
|
3440
|
-
|
|
3591
|
+
buffer = Buffer.allocUnsafeSlow(newSize);
|
|
3592
|
+
// assign resized buffer to local sharedBuffer
|
|
3593
|
+
if (buffer === this.sharedBuffer) {
|
|
3594
|
+
this.sharedBuffer = buffer;
|
|
3595
|
+
}
|
|
3596
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
3441
3597
|
}
|
|
3442
3598
|
else {
|
|
3443
3599
|
//
|
|
3444
3600
|
// only clear changes after making sure buffer resize is not required.
|
|
3445
3601
|
//
|
|
3446
|
-
if (
|
|
3602
|
+
if (shouldClearChanges) {
|
|
3447
3603
|
//
|
|
3448
3604
|
// FIXME: avoid iterating over change trees twice.
|
|
3449
3605
|
//
|
|
3450
3606
|
this.onEndEncode(changeTrees);
|
|
3451
3607
|
}
|
|
3452
|
-
|
|
3453
|
-
return bytes.slice(0, it.offset);
|
|
3608
|
+
return buffer.subarray(0, it.offset);
|
|
3454
3609
|
}
|
|
3455
3610
|
}
|
|
3456
|
-
encodeAll(it = { offset: 0 }) {
|
|
3457
|
-
// console.log(
|
|
3458
|
-
//
|
|
3459
|
-
|
|
3460
|
-
// });
|
|
3461
|
-
return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
|
|
3611
|
+
encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
|
|
3612
|
+
// console.log(`\nencodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
3613
|
+
// this.debugChanges("allChanges");
|
|
3614
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
3462
3615
|
}
|
|
3463
3616
|
encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3464
3617
|
const viewOffset = it.offset;
|
|
3465
|
-
// console.log(
|
|
3466
|
-
// this.
|
|
3618
|
+
// console.log(`\nencodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
3619
|
+
// this.debugChanges("allFilteredChanges");
|
|
3467
3620
|
// try to encode "filtered" changes
|
|
3468
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
3621
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true, viewOffset);
|
|
3469
3622
|
return Buffer.concat([
|
|
3470
|
-
bytes.
|
|
3471
|
-
bytes.
|
|
3623
|
+
bytes.subarray(0, sharedOffset),
|
|
3624
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3472
3625
|
]);
|
|
3473
3626
|
}
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3627
|
+
debugChanges(field) {
|
|
3628
|
+
const changeSet = (typeof (field) === "string")
|
|
3629
|
+
? this.root[field]
|
|
3630
|
+
: field;
|
|
3631
|
+
Array.from(changeSet.entries()).map((item) => {
|
|
3632
|
+
const metadata = item[0].ref.constructor[Symbol.metadata];
|
|
3633
|
+
console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
3634
|
+
item[1].forEach((op, index) => {
|
|
3635
|
+
console.log(" ->", {
|
|
3636
|
+
index,
|
|
3637
|
+
field: metadata?.[index],
|
|
3638
|
+
op: OPERATION[op],
|
|
3639
|
+
});
|
|
3640
|
+
});
|
|
3641
|
+
});
|
|
3642
|
+
}
|
|
3484
3643
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3485
3644
|
const viewOffset = it.offset;
|
|
3486
|
-
//
|
|
3487
|
-
this.
|
|
3645
|
+
// console.log(`\nencodeView(), view.changes (${view.changes.size})`);
|
|
3646
|
+
// this.debugChanges(view.changes);
|
|
3647
|
+
// console.log(`\nencodeView(), this.root.filteredChanges (${this.root.filteredChanges.size})`);
|
|
3648
|
+
// this.debugChanges("filteredChanges");
|
|
3488
3649
|
// encode visibility changes (add/remove for this view)
|
|
3489
3650
|
const viewChangesIterator = view.changes.entries();
|
|
3490
3651
|
for (const [changeTree, changes] of viewChangesIterator) {
|
|
@@ -3511,15 +3672,22 @@ class Encoder {
|
|
|
3511
3672
|
//
|
|
3512
3673
|
// clear "view" changes after encoding
|
|
3513
3674
|
view.changes.clear();
|
|
3675
|
+
// try to encode "filtered" changes
|
|
3676
|
+
this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
|
|
3514
3677
|
return Buffer.concat([
|
|
3515
|
-
bytes.
|
|
3516
|
-
bytes.
|
|
3678
|
+
bytes.subarray(0, sharedOffset),
|
|
3679
|
+
bytes.subarray(viewOffset, it.offset)
|
|
3517
3680
|
]);
|
|
3518
3681
|
}
|
|
3519
3682
|
onEndEncode(changeTrees = this.root.changes) {
|
|
3520
3683
|
const changeTreesIterator = changeTrees.entries();
|
|
3521
3684
|
for (const [changeTree, _] of changeTreesIterator) {
|
|
3522
3685
|
changeTree.endEncode();
|
|
3686
|
+
// changeTree.changes.clear();
|
|
3687
|
+
// // ArraySchema and MapSchema have a custom "encode end" method
|
|
3688
|
+
// changeTree.ref[$onEncodeEnd]?.();
|
|
3689
|
+
// // Not a new instance anymore
|
|
3690
|
+
// delete changeTree[$isNew];
|
|
3523
3691
|
}
|
|
3524
3692
|
}
|
|
3525
3693
|
discardChanges() {
|
|
@@ -3635,8 +3803,9 @@ class ReferenceTracker {
|
|
|
3635
3803
|
// Ensure child schema instances have their references removed as well.
|
|
3636
3804
|
//
|
|
3637
3805
|
if (Metadata.isValidInstance(ref)) {
|
|
3638
|
-
const metadata = ref
|
|
3639
|
-
for (const
|
|
3806
|
+
const metadata = ref.constructor[Symbol.metadata];
|
|
3807
|
+
for (const index in metadata) {
|
|
3808
|
+
const field = metadata[index].name;
|
|
3640
3809
|
const childRefId = typeof (ref[field]) === "object" && this.refIds.get(ref[field]);
|
|
3641
3810
|
if (childRefId) {
|
|
3642
3811
|
this.removeRef(childRefId);
|
|
@@ -3683,21 +3852,21 @@ class ReferenceTracker {
|
|
|
3683
3852
|
class Decoder {
|
|
3684
3853
|
constructor(root, context) {
|
|
3685
3854
|
this.currentRefId = 0;
|
|
3686
|
-
this.
|
|
3855
|
+
this.setState(root);
|
|
3687
3856
|
this.context = context || new TypeContext(root.constructor);
|
|
3688
3857
|
// console.log(">>>>>>>>>>>>>>>> Decoder types");
|
|
3689
3858
|
// this.context.schemas.forEach((id, schema) => {
|
|
3690
3859
|
// console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
|
|
3691
3860
|
// });
|
|
3692
3861
|
}
|
|
3693
|
-
|
|
3862
|
+
setState(root) {
|
|
3694
3863
|
this.state = root;
|
|
3695
|
-
this
|
|
3696
|
-
this
|
|
3864
|
+
this.root = new ReferenceTracker();
|
|
3865
|
+
this.root.addRef(0, root);
|
|
3697
3866
|
}
|
|
3698
3867
|
decode(bytes, it = { offset: 0 }, ref = this.state) {
|
|
3699
3868
|
const allChanges = [];
|
|
3700
|
-
const $root = this
|
|
3869
|
+
const $root = this.root;
|
|
3701
3870
|
const totalBytes = bytes.byteLength;
|
|
3702
3871
|
let decoder = ref['constructor'][$decoder];
|
|
3703
3872
|
this.currentRefId = 0;
|
|
@@ -3778,7 +3947,7 @@ class Decoder {
|
|
|
3778
3947
|
previousValue: value
|
|
3779
3948
|
});
|
|
3780
3949
|
if (needRemoveRef) {
|
|
3781
|
-
this
|
|
3950
|
+
this.root.removeRef(this.root.refIds.get(value));
|
|
3782
3951
|
}
|
|
3783
3952
|
});
|
|
3784
3953
|
}
|
|
@@ -3818,14 +3987,14 @@ class Reflection extends Schema {
|
|
|
3818
3987
|
super(...arguments);
|
|
3819
3988
|
this.types = new ArraySchema();
|
|
3820
3989
|
}
|
|
3821
|
-
static encode(instance, context) {
|
|
3822
|
-
|
|
3823
|
-
context = new TypeContext(instance.constructor);
|
|
3824
|
-
}
|
|
3990
|
+
static encode(instance, context, it = { offset: 0 }) {
|
|
3991
|
+
context ??= new TypeContext(instance.constructor);
|
|
3825
3992
|
const reflection = new Reflection();
|
|
3826
3993
|
const encoder = new Encoder(reflection);
|
|
3827
3994
|
const buildType = (currentType, metadata) => {
|
|
3828
|
-
for (const
|
|
3995
|
+
for (const fieldIndex in metadata) {
|
|
3996
|
+
const index = Number(fieldIndex);
|
|
3997
|
+
const fieldName = metadata[index].name;
|
|
3829
3998
|
// skip fields from parent classes
|
|
3830
3999
|
if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
|
|
3831
4000
|
continue;
|
|
@@ -3833,7 +4002,7 @@ class Reflection extends Schema {
|
|
|
3833
4002
|
const field = new ReflectionField();
|
|
3834
4003
|
field.name = fieldName;
|
|
3835
4004
|
let fieldType;
|
|
3836
|
-
const type = metadata[
|
|
4005
|
+
const type = metadata[index].type;
|
|
3837
4006
|
if (typeof (type) === "string") {
|
|
3838
4007
|
fieldType = type;
|
|
3839
4008
|
}
|
|
@@ -3875,7 +4044,6 @@ class Reflection extends Schema {
|
|
|
3875
4044
|
}
|
|
3876
4045
|
buildType(type, klass[Symbol.metadata]);
|
|
3877
4046
|
}
|
|
3878
|
-
const it = { offset: 0 };
|
|
3879
4047
|
const buf = encoder.encodeAll(it);
|
|
3880
4048
|
return Buffer.from(buf, 0, it.offset);
|
|
3881
4049
|
}
|
|
@@ -3883,59 +4051,304 @@ class Reflection extends Schema {
|
|
|
3883
4051
|
const reflection = new Reflection();
|
|
3884
4052
|
const reflectionDecoder = new Decoder(reflection);
|
|
3885
4053
|
reflectionDecoder.decode(bytes, it);
|
|
3886
|
-
const
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
const
|
|
4054
|
+
const typeContext = new TypeContext();
|
|
4055
|
+
// 1st pass, initialize metadata + inheritance
|
|
4056
|
+
reflection.types.forEach((reflectionType) => {
|
|
4057
|
+
const parentClass = typeContext.get(reflectionType.extendsId) ?? Schema;
|
|
4058
|
+
const schema = class _ extends parentClass {
|
|
3890
4059
|
};
|
|
3891
|
-
|
|
3892
|
-
const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
|
|
3893
|
-
Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
4060
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
3894
4061
|
// register for inheritance support
|
|
3895
4062
|
TypeContext.register(schema);
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
return types;
|
|
4063
|
+
// for inheritance support
|
|
4064
|
+
Metadata.initialize(schema, parentMetadata);
|
|
4065
|
+
typeContext.add(schema, reflectionType.id);
|
|
3900
4066
|
}, {});
|
|
4067
|
+
// 2nd pass, set fields
|
|
3901
4068
|
reflection.types.forEach((reflectionType) => {
|
|
3902
|
-
const schemaType =
|
|
4069
|
+
const schemaType = typeContext.get(reflectionType.id);
|
|
3903
4070
|
const metadata = schemaType[Symbol.metadata];
|
|
3904
|
-
const
|
|
3905
|
-
const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
|
|
4071
|
+
const parentFieldIndex = 0;
|
|
3906
4072
|
reflectionType.fields.forEach((field, i) => {
|
|
3907
4073
|
const fieldIndex = parentFieldIndex + i;
|
|
3908
4074
|
if (field.referencedType !== undefined) {
|
|
3909
4075
|
let fieldType = field.type;
|
|
3910
|
-
let refType =
|
|
4076
|
+
let refType = typeContext.get(field.referencedType);
|
|
3911
4077
|
// map or array of primitive type (-1)
|
|
3912
4078
|
if (!refType) {
|
|
3913
4079
|
const typeInfo = field.type.split(":");
|
|
3914
4080
|
fieldType = typeInfo[0];
|
|
3915
|
-
refType = typeInfo[1];
|
|
4081
|
+
refType = typeInfo[1]; // string
|
|
3916
4082
|
}
|
|
3917
4083
|
if (fieldType === "ref") {
|
|
3918
|
-
// type(refType)(schemaType.prototype, field.name);
|
|
3919
4084
|
Metadata.addField(metadata, fieldIndex, field.name, refType);
|
|
3920
4085
|
}
|
|
3921
4086
|
else {
|
|
3922
|
-
// type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
|
|
3923
4087
|
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
|
|
3924
4088
|
}
|
|
3925
4089
|
}
|
|
3926
4090
|
else {
|
|
3927
|
-
// type(field.type as PrimitiveType)(schemaType.prototype, field.name);
|
|
3928
4091
|
Metadata.addField(metadata, fieldIndex, field.name, field.type);
|
|
3929
4092
|
}
|
|
3930
4093
|
});
|
|
3931
4094
|
});
|
|
3932
|
-
|
|
4095
|
+
// @ts-ignore
|
|
4096
|
+
return new (typeContext.get(0))();
|
|
3933
4097
|
}
|
|
3934
4098
|
}
|
|
3935
4099
|
__decorate([
|
|
3936
4100
|
type([ReflectionType])
|
|
3937
4101
|
], Reflection.prototype, "types", void 0);
|
|
3938
4102
|
|
|
4103
|
+
function getDecoderStateCallbacks(decoder) {
|
|
4104
|
+
const $root = decoder.root;
|
|
4105
|
+
const callbacks = $root.callbacks;
|
|
4106
|
+
const onAddCalls = new WeakMap();
|
|
4107
|
+
let currentOnAddCallback;
|
|
4108
|
+
decoder.triggerChanges = function (allChanges) {
|
|
4109
|
+
const uniqueRefIds = new Set();
|
|
4110
|
+
for (let i = 0, l = allChanges.length; i < l; i++) {
|
|
4111
|
+
const change = allChanges[i];
|
|
4112
|
+
const refId = change.refId;
|
|
4113
|
+
const ref = change.ref;
|
|
4114
|
+
const $callbacks = callbacks[refId];
|
|
4115
|
+
if (!$callbacks) {
|
|
4116
|
+
continue;
|
|
4117
|
+
}
|
|
4118
|
+
//
|
|
4119
|
+
// trigger onRemove on child structure.
|
|
4120
|
+
//
|
|
4121
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE &&
|
|
4122
|
+
change.previousValue instanceof Schema) {
|
|
4123
|
+
const deleteCallbacks = callbacks[$root.refIds.get(change.previousValue)]?.[OPERATION.DELETE];
|
|
4124
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4125
|
+
deleteCallbacks[i]();
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
if (ref instanceof Schema) {
|
|
4129
|
+
//
|
|
4130
|
+
// Handle schema instance
|
|
4131
|
+
//
|
|
4132
|
+
if (!uniqueRefIds.has(refId)) {
|
|
4133
|
+
// trigger onChange
|
|
4134
|
+
const replaceCallbacks = $callbacks?.[OPERATION.REPLACE];
|
|
4135
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4136
|
+
replaceCallbacks[i]();
|
|
4137
|
+
// try {
|
|
4138
|
+
// } catch (e) {
|
|
4139
|
+
// console.error(e);
|
|
4140
|
+
// }
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
if ($callbacks.hasOwnProperty(change.field)) {
|
|
4144
|
+
const fieldCallbacks = $callbacks[change.field];
|
|
4145
|
+
for (let i = fieldCallbacks?.length - 1; i >= 0; i--) {
|
|
4146
|
+
fieldCallbacks[i](change.value, change.previousValue);
|
|
4147
|
+
// try {
|
|
4148
|
+
// } catch (e) {
|
|
4149
|
+
// console.error(e);
|
|
4150
|
+
// }
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
else {
|
|
4155
|
+
//
|
|
4156
|
+
// Handle collection of items
|
|
4157
|
+
//
|
|
4158
|
+
if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
|
|
4159
|
+
//
|
|
4160
|
+
// FIXME: `previousValue` should always be available.
|
|
4161
|
+
//
|
|
4162
|
+
if (change.previousValue !== undefined) {
|
|
4163
|
+
// triger onRemove
|
|
4164
|
+
const deleteCallbacks = $callbacks[OPERATION.DELETE];
|
|
4165
|
+
for (let i = deleteCallbacks?.length - 1; i >= 0; i--) {
|
|
4166
|
+
deleteCallbacks[i](change.previousValue, change.dynamicIndex ?? change.field);
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
// Handle DELETE_AND_ADD operations
|
|
4170
|
+
if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
|
|
4171
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4172
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4173
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
|
|
4178
|
+
// triger onAdd
|
|
4179
|
+
const addCallbacks = $callbacks[OPERATION.ADD];
|
|
4180
|
+
for (let i = addCallbacks?.length - 1; i >= 0; i--) {
|
|
4181
|
+
addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
// trigger onChange
|
|
4185
|
+
if (change.value !== change.previousValue) {
|
|
4186
|
+
const replaceCallbacks = $callbacks[OPERATION.REPLACE];
|
|
4187
|
+
for (let i = replaceCallbacks?.length - 1; i >= 0; i--) {
|
|
4188
|
+
replaceCallbacks[i](change.value, change.dynamicIndex ?? change.field);
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
uniqueRefIds.add(refId);
|
|
4193
|
+
}
|
|
4194
|
+
};
|
|
4195
|
+
function getProxy(metadataOrType, context) {
|
|
4196
|
+
let metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
|
|
4197
|
+
let isCollection = ((context.instance && typeof (context.instance['forEach']) === "function") ||
|
|
4198
|
+
(metadataOrType && typeof (metadataOrType[Symbol.metadata]) === "undefined"));
|
|
4199
|
+
if (metadata && !isCollection) {
|
|
4200
|
+
const onAddListen = function (ref, prop, callback, immediate) {
|
|
4201
|
+
// immediate trigger
|
|
4202
|
+
if (immediate &&
|
|
4203
|
+
context.instance[prop] !== undefined &&
|
|
4204
|
+
!onAddCalls.has(currentOnAddCallback) // Workaround for https://github.com/colyseus/schema/issues/147
|
|
4205
|
+
) {
|
|
4206
|
+
callback(context.instance[prop], undefined);
|
|
4207
|
+
}
|
|
4208
|
+
return $root.addCallback($root.refIds.get(ref), prop, callback);
|
|
4209
|
+
};
|
|
4210
|
+
/**
|
|
4211
|
+
* Schema instances
|
|
4212
|
+
*/
|
|
4213
|
+
return new Proxy({
|
|
4214
|
+
listen: function listen(prop, callback, immediate = true) {
|
|
4215
|
+
if (context.instance) {
|
|
4216
|
+
return onAddListen(context.instance, prop, callback, immediate);
|
|
4217
|
+
}
|
|
4218
|
+
else {
|
|
4219
|
+
// collection instance not received yet
|
|
4220
|
+
let detachCallback = () => { };
|
|
4221
|
+
context.onInstanceAvailable((ref, existing) => {
|
|
4222
|
+
detachCallback = onAddListen(ref, prop, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
4223
|
+
});
|
|
4224
|
+
return () => detachCallback();
|
|
4225
|
+
}
|
|
4226
|
+
},
|
|
4227
|
+
onChange: function onChange(callback) {
|
|
4228
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, callback);
|
|
4229
|
+
},
|
|
4230
|
+
//
|
|
4231
|
+
// TODO: refactor `bindTo()` implementation.
|
|
4232
|
+
// There is room for improvement.
|
|
4233
|
+
//
|
|
4234
|
+
bindTo: function bindTo(targetObject, properties) {
|
|
4235
|
+
if (!properties) {
|
|
4236
|
+
properties = Object.keys(metadata).map((index) => metadata[index].name);
|
|
4237
|
+
}
|
|
4238
|
+
return $root.addCallback($root.refIds.get(context.instance), OPERATION.REPLACE, () => {
|
|
4239
|
+
properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
|
|
4240
|
+
});
|
|
4241
|
+
}
|
|
4242
|
+
}, {
|
|
4243
|
+
get(target, prop) {
|
|
4244
|
+
const metadataField = metadata[metadata[prop]];
|
|
4245
|
+
if (metadataField) {
|
|
4246
|
+
const instance = context.instance?.[prop];
|
|
4247
|
+
const onInstanceAvailable = ((callback) => {
|
|
4248
|
+
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
4249
|
+
callback(value, false);
|
|
4250
|
+
// FIXME: by "unbinding" the callback here,
|
|
4251
|
+
// it will not support when the server
|
|
4252
|
+
// re-instantiates the instance.
|
|
4253
|
+
//
|
|
4254
|
+
unbind?.();
|
|
4255
|
+
}, false);
|
|
4256
|
+
// has existing value
|
|
4257
|
+
if ($root.refIds.get(instance) !== undefined) {
|
|
4258
|
+
callback(instance, true);
|
|
4259
|
+
}
|
|
4260
|
+
});
|
|
4261
|
+
return getProxy(metadataField.type, {
|
|
4262
|
+
// make sure refId is available, otherwise need to wait for the instance to be available.
|
|
4263
|
+
instance: ($root.refIds.get(instance) && instance),
|
|
4264
|
+
parentInstance: context.instance,
|
|
4265
|
+
onInstanceAvailable,
|
|
4266
|
+
});
|
|
4267
|
+
}
|
|
4268
|
+
else {
|
|
4269
|
+
// accessing the function
|
|
4270
|
+
return target[prop];
|
|
4271
|
+
}
|
|
4272
|
+
},
|
|
4273
|
+
has(target, prop) { return metadata[prop] !== undefined; },
|
|
4274
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4275
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4276
|
+
});
|
|
4277
|
+
}
|
|
4278
|
+
else {
|
|
4279
|
+
/**
|
|
4280
|
+
* Collection instances
|
|
4281
|
+
*/
|
|
4282
|
+
const onAdd = function (ref, callback, immediate) {
|
|
4283
|
+
// Trigger callback on existing items
|
|
4284
|
+
if (immediate) {
|
|
4285
|
+
ref.forEach((v, k) => callback(v, k));
|
|
4286
|
+
}
|
|
4287
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, (value, key) => {
|
|
4288
|
+
onAddCalls.set(callback, true);
|
|
4289
|
+
currentOnAddCallback = callback;
|
|
4290
|
+
callback(value, key);
|
|
4291
|
+
onAddCalls.delete(callback);
|
|
4292
|
+
currentOnAddCallback = undefined;
|
|
4293
|
+
});
|
|
4294
|
+
};
|
|
4295
|
+
const onRemove = function (ref, callback) {
|
|
4296
|
+
return $root.addCallback($root.refIds.get(ref), OPERATION.DELETE, callback);
|
|
4297
|
+
};
|
|
4298
|
+
return new Proxy({
|
|
4299
|
+
onAdd: function (callback, immediate = true) {
|
|
4300
|
+
//
|
|
4301
|
+
// https://github.com/colyseus/schema/issues/147
|
|
4302
|
+
// If parent instance has "onAdd" registered, avoid triggering immediate callback.
|
|
4303
|
+
//
|
|
4304
|
+
if (context.instance) {
|
|
4305
|
+
return onAdd(context.instance, callback, immediate && !onAddCalls.has(currentOnAddCallback));
|
|
4306
|
+
}
|
|
4307
|
+
else if (context.onInstanceAvailable) {
|
|
4308
|
+
// collection instance not received yet
|
|
4309
|
+
let detachCallback = () => { };
|
|
4310
|
+
context.onInstanceAvailable((ref, existing) => {
|
|
4311
|
+
detachCallback = onAdd(ref, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
|
|
4312
|
+
});
|
|
4313
|
+
return () => detachCallback();
|
|
4314
|
+
}
|
|
4315
|
+
},
|
|
4316
|
+
onRemove: function (callback) {
|
|
4317
|
+
if (context.onInstanceAvailable) {
|
|
4318
|
+
// collection instance not received yet
|
|
4319
|
+
let detachCallback = () => { };
|
|
4320
|
+
context.onInstanceAvailable((ref) => {
|
|
4321
|
+
detachCallback = onRemove(ref, callback);
|
|
4322
|
+
});
|
|
4323
|
+
return () => detachCallback();
|
|
4324
|
+
}
|
|
4325
|
+
else if (context.instance) {
|
|
4326
|
+
return onRemove(context.instance, callback);
|
|
4327
|
+
}
|
|
4328
|
+
},
|
|
4329
|
+
}, {
|
|
4330
|
+
get(target, prop) {
|
|
4331
|
+
if (!target[prop]) {
|
|
4332
|
+
throw new Error(`Can't access '${prop}' through callback proxy. access the instance directly.`);
|
|
4333
|
+
}
|
|
4334
|
+
return target[prop];
|
|
4335
|
+
},
|
|
4336
|
+
has(target, prop) { return target[prop] !== undefined; },
|
|
4337
|
+
set(_, _1, _2) { throw new Error("not allowed"); },
|
|
4338
|
+
deleteProperty(_, _1) { throw new Error("not allowed"); },
|
|
4339
|
+
});
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
function $(instance) {
|
|
4343
|
+
return getProxy(undefined, { instance });
|
|
4344
|
+
}
|
|
4345
|
+
return $;
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
function getRawChangesCallback(decoder, callback) {
|
|
4349
|
+
decoder.triggerChanges = callback;
|
|
4350
|
+
}
|
|
4351
|
+
|
|
3939
4352
|
class StateView {
|
|
3940
4353
|
constructor() {
|
|
3941
4354
|
/**
|
|
@@ -3953,20 +4366,21 @@ class StateView {
|
|
|
3953
4366
|
this.changes = new Map();
|
|
3954
4367
|
}
|
|
3955
4368
|
// TODO: allow to set multiple tags at once
|
|
3956
|
-
add(obj, tag = DEFAULT_VIEW_TAG) {
|
|
4369
|
+
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
3957
4370
|
if (!obj[$changes]) {
|
|
3958
4371
|
console.warn("StateView#add(), invalid object:", obj);
|
|
3959
4372
|
return this;
|
|
3960
4373
|
}
|
|
3961
|
-
|
|
3962
|
-
this.items.add(changeTree);
|
|
3963
|
-
// Add children of this ChangeTree to this view
|
|
3964
|
-
changeTree.forEachChild((change, _) => this.add(change.ref, tag));
|
|
3965
|
-
// FIXME: ArraySchema/MapSchema does not have metadata
|
|
4374
|
+
// FIXME: ArraySchema/MapSchema do not have metadata
|
|
3966
4375
|
const metadata = obj.constructor[Symbol.metadata];
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
4376
|
+
const changeTree = obj[$changes];
|
|
4377
|
+
this.items.add(changeTree);
|
|
4378
|
+
// add parent ChangeTree's
|
|
4379
|
+
// - if it was invisible to this view
|
|
4380
|
+
// - if it were previously filtered out
|
|
4381
|
+
if (checkIncludeParent && changeTree.parent) {
|
|
4382
|
+
this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
|
|
4383
|
+
}
|
|
3970
4384
|
//
|
|
3971
4385
|
// TODO: when adding an item of a MapSchema, the changes may not
|
|
3972
4386
|
// be set (only the parent's changes are set)
|
|
@@ -3990,7 +4404,6 @@ class StateView {
|
|
|
3990
4404
|
tags = this.tags.get(changeTree);
|
|
3991
4405
|
}
|
|
3992
4406
|
tags.add(tag);
|
|
3993
|
-
// console.log("BY TAG:", tag);
|
|
3994
4407
|
// Ref: add tagged properties
|
|
3995
4408
|
metadata?.[-3]?.[tag]?.forEach((index) => {
|
|
3996
4409
|
if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
@@ -3999,73 +4412,63 @@ class StateView {
|
|
|
3999
4412
|
});
|
|
4000
4413
|
}
|
|
4001
4414
|
else {
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
// metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
|
|
4005
|
-
// if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
4006
|
-
// changes.set(index, OPERATION.ADD);
|
|
4007
|
-
// }
|
|
4008
|
-
// });
|
|
4009
|
-
const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4415
|
+
const isInvisible = this.invisible.has(changeTree);
|
|
4416
|
+
const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
|
|
4010
4417
|
? changeTree.allFilteredChanges
|
|
4011
4418
|
: changeTree.allChanges;
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4419
|
+
changeSet.forEach((op, index) => {
|
|
4420
|
+
const tagAtIndex = metadata?.[index].tag;
|
|
4421
|
+
if ((isInvisible || // if "invisible", include all
|
|
4422
|
+
tagAtIndex === undefined || // "all change" with no tag
|
|
4423
|
+
tagAtIndex === tag // tagged property
|
|
4424
|
+
) &&
|
|
4425
|
+
op !== OPERATION.DELETE) {
|
|
4426
|
+
changes.set(index, op);
|
|
4018
4427
|
}
|
|
4019
|
-
}
|
|
4020
|
-
}
|
|
4021
|
-
// TODO: avoid unnecessary iteration here
|
|
4022
|
-
while (changeTree.parent &&
|
|
4023
|
-
(changeTree = changeTree.parent[$changes]) &&
|
|
4024
|
-
(changeTree.isFiltered || changeTree.isPartiallyFiltered)) {
|
|
4025
|
-
this.items.add(changeTree);
|
|
4428
|
+
});
|
|
4026
4429
|
}
|
|
4430
|
+
// Add children of this ChangeTree to this view
|
|
4431
|
+
changeTree.forEachChild((change, index) => {
|
|
4432
|
+
// Do not ADD children that don't have the same tag
|
|
4433
|
+
if (metadata && metadata[index].tag !== tag) {
|
|
4434
|
+
return;
|
|
4435
|
+
}
|
|
4436
|
+
this.add(change.ref, tag, false);
|
|
4437
|
+
});
|
|
4027
4438
|
return this;
|
|
4028
4439
|
}
|
|
4029
|
-
addParent(changeTree, tag) {
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4440
|
+
addParent(changeTree, parentIndex, tag) {
|
|
4441
|
+
// view must have all "changeTree" parent tree
|
|
4442
|
+
this.items.add(changeTree);
|
|
4443
|
+
// add parent's parent
|
|
4444
|
+
const parentChangeTree = changeTree.parent?.[$changes];
|
|
4445
|
+
if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
|
|
4446
|
+
this.addParent(parentChangeTree, changeTree.parentIndex, tag);
|
|
4033
4447
|
}
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
if (!this.invisible.has(parentChangeTree)) {
|
|
4037
|
-
// parent is already available, no need to add it!
|
|
4448
|
+
// parent is already available, no need to add it!
|
|
4449
|
+
if (!this.invisible.has(changeTree)) {
|
|
4038
4450
|
return;
|
|
4039
4451
|
}
|
|
4040
|
-
this.addParent(parentChangeTree, tag);
|
|
4041
4452
|
// add parent's tag properties
|
|
4042
|
-
if (
|
|
4043
|
-
let
|
|
4044
|
-
if (
|
|
4045
|
-
|
|
4046
|
-
this.changes.set(
|
|
4453
|
+
if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
|
|
4454
|
+
let changes = this.changes.get(changeTree);
|
|
4455
|
+
if (changes === undefined) {
|
|
4456
|
+
changes = new Map();
|
|
4457
|
+
this.changes.set(changeTree, changes);
|
|
4047
4458
|
}
|
|
4048
|
-
// console.log("add parent change", {
|
|
4049
|
-
// parentIndex,
|
|
4050
|
-
// parentChanges,
|
|
4051
|
-
// parentChange: (
|
|
4052
|
-
// parentChangeTree.getChange(parentIndex) &&
|
|
4053
|
-
// OPERATION[parentChangeTree.getChange(parentIndex)]
|
|
4054
|
-
// ),
|
|
4055
|
-
// })
|
|
4056
4459
|
if (!this.tags) {
|
|
4057
4460
|
this.tags = new WeakMap();
|
|
4058
4461
|
}
|
|
4059
4462
|
let tags;
|
|
4060
|
-
if (!this.tags.has(
|
|
4463
|
+
if (!this.tags.has(changeTree)) {
|
|
4061
4464
|
tags = new Set();
|
|
4062
|
-
this.tags.set(
|
|
4465
|
+
this.tags.set(changeTree, tags);
|
|
4063
4466
|
}
|
|
4064
4467
|
else {
|
|
4065
|
-
tags = this.tags.get(
|
|
4468
|
+
tags = this.tags.get(changeTree);
|
|
4066
4469
|
}
|
|
4067
4470
|
tags.add(tag);
|
|
4068
|
-
|
|
4471
|
+
changes.set(parentIndex, OPERATION.ADD);
|
|
4069
4472
|
}
|
|
4070
4473
|
}
|
|
4071
4474
|
remove(obj, tag = DEFAULT_VIEW_TAG) {
|
|
@@ -4129,5 +4532,5 @@ registerType("array", { constructor: ArraySchema });
|
|
|
4129
4532
|
registerType("set", { constructor: SetSchema });
|
|
4130
4533
|
registerType("collection", { constructor: CollectionSchema, });
|
|
4131
4534
|
|
|
4132
|
-
export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, registerType, type, view };
|
|
4535
|
+
export { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track, ArraySchema, ChangeTree, CollectionSchema, Decoder, Encoder, MapSchema, Metadata, OPERATION, Reflection, ReflectionField, ReflectionType, Schema, SetSchema, StateView, TypeContext, decode, decodeKeyValueOperation, decodeSchemaOperation, defineTypes, deprecated, dumpChanges, encode, encodeArray as encodeKeyValueOperation, encodeSchemaOperation, getDecoderStateCallbacks, getRawChangesCallback, registerType, type, view };
|
|
4133
4536
|
//# sourceMappingURL=index.mjs.map
|