@colyseus/schema 3.0.0-alpha.30 → 3.0.0-alpha.32
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/build/cjs/index.js +389 -354
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +389 -354
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +389 -354
- package/lib/Metadata.d.ts +14 -5
- package/lib/Metadata.js +49 -20
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.js +4 -13
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.js +26 -39
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +1 -2
- package/lib/annotations.js +58 -52
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.js +25 -22
- package/lib/bench_encode.js.map +1 -1
- package/lib/decoder/DecodeOperation.js +7 -9
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/ReferenceTracker.js +3 -2
- package/lib/decoder/ReferenceTracker.js.map +1 -1
- package/lib/decoder/strategy/StateCallbacks.js +4 -3
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +8 -7
- package/lib/encoder/ChangeTree.js +135 -117
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +1 -4
- package/lib/encoder/EncodeOperation.js +17 -47
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.js +18 -6
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +2 -2
- package/lib/encoder/Root.js +18 -6
- package/lib/encoder/Root.js.map +1 -1
- package/lib/encoder/StateView.js +3 -3
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/assert.d.ts +2 -1
- package/lib/encoding/assert.js +2 -2
- package/lib/encoding/assert.js.map +1 -1
- package/lib/index.d.ts +1 -2
- package/lib/index.js +11 -10
- package/lib/index.js.map +1 -1
- package/lib/types/TypeContext.js +7 -14
- package/lib/types/TypeContext.js.map +1 -1
- package/lib/types/custom/ArraySchema.js +6 -0
- 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/symbols.d.ts +1 -0
- package/lib/types/symbols.js +2 -1
- package/lib/types/symbols.js.map +1 -1
- package/package.json +1 -1
- package/src/Metadata.ts +60 -29
- package/src/Reflection.ts +5 -15
- package/src/Schema.ts +33 -45
- package/src/annotations.ts +75 -67
- package/src/bench_encode.ts +29 -27
- package/src/decoder/DecodeOperation.ts +12 -11
- package/src/decoder/ReferenceTracker.ts +3 -2
- package/src/decoder/strategy/StateCallbacks.ts +4 -3
- package/src/encoder/ChangeTree.ts +154 -138
- package/src/encoder/EncodeOperation.ts +42 -62
- package/src/encoder/Encoder.ts +25 -8
- package/src/encoder/Root.ts +23 -6
- package/src/encoder/StateView.ts +4 -4
- package/src/encoding/assert.ts +4 -3
- package/src/index.ts +1 -4
- package/src/types/TypeContext.ts +10 -15
- package/src/types/custom/ArraySchema.ts +8 -0
- 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/cjs/index.js
CHANGED
|
@@ -36,6 +36,7 @@ const $decoder = Symbol("$decoder");
|
|
|
36
36
|
const $filter = Symbol("$filter");
|
|
37
37
|
const $getByIndex = Symbol("$getByIndex");
|
|
38
38
|
const $deleteByIndex = Symbol("$deleteByIndex");
|
|
39
|
+
const $descriptors = Symbol("$descriptors");
|
|
39
40
|
/**
|
|
40
41
|
* Used to hold ChangeTree instances whitin the structures
|
|
41
42
|
*/
|
|
@@ -71,34 +72,67 @@ function getType(identifier) {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
const Metadata = {
|
|
74
|
-
addField(metadata, index,
|
|
75
|
+
addField(metadata, index, name, type, descriptor) {
|
|
75
76
|
if (index > 64) {
|
|
76
|
-
throw new Error(`Can't define field '${
|
|
77
|
+
throw new Error(`Can't define field '${name}'.\nSchema instances may only have up to 64 fields.`);
|
|
77
78
|
}
|
|
78
|
-
metadata[
|
|
79
|
+
metadata[index] = Object.assign(metadata[index] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
|
|
79
80
|
{
|
|
80
81
|
type: (Array.isArray(type))
|
|
81
82
|
? { array: type[0] }
|
|
82
83
|
: type,
|
|
83
84
|
index,
|
|
84
|
-
|
|
85
|
+
name,
|
|
85
86
|
});
|
|
87
|
+
// create "descriptors" map
|
|
88
|
+
metadata[$descriptors] ??= {};
|
|
89
|
+
if (descriptor) {
|
|
90
|
+
// for encoder
|
|
91
|
+
metadata[$descriptors][name] = descriptor;
|
|
92
|
+
metadata[$descriptors][`_${name}`] = {
|
|
93
|
+
value: undefined,
|
|
94
|
+
writable: true,
|
|
95
|
+
enumerable: false,
|
|
96
|
+
configurable: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// for decoder
|
|
101
|
+
metadata[$descriptors][name] = {
|
|
102
|
+
value: undefined,
|
|
103
|
+
writable: true,
|
|
104
|
+
enumerable: true,
|
|
105
|
+
configurable: true,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
86
108
|
// map -1 as last field index
|
|
87
109
|
Object.defineProperty(metadata, -1, {
|
|
88
110
|
value: index,
|
|
89
111
|
enumerable: false,
|
|
90
112
|
configurable: true
|
|
91
113
|
});
|
|
92
|
-
// map
|
|
93
|
-
Object.defineProperty(metadata,
|
|
94
|
-
value:
|
|
114
|
+
// map field name => index (non enumerable)
|
|
115
|
+
Object.defineProperty(metadata, name, {
|
|
116
|
+
value: index,
|
|
95
117
|
enumerable: false,
|
|
96
118
|
configurable: true,
|
|
97
119
|
});
|
|
120
|
+
// if child Ref/complex type, add to -4
|
|
121
|
+
if (typeof (metadata[index].type) !== "string") {
|
|
122
|
+
if (metadata[-4] === undefined) {
|
|
123
|
+
Object.defineProperty(metadata, -4, {
|
|
124
|
+
value: [],
|
|
125
|
+
enumerable: false,
|
|
126
|
+
configurable: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
metadata[-4].push(index);
|
|
130
|
+
}
|
|
98
131
|
},
|
|
99
132
|
setTag(metadata, fieldName, tag) {
|
|
133
|
+
const index = metadata[fieldName];
|
|
134
|
+
const field = metadata[index];
|
|
100
135
|
// add 'tag' to the field
|
|
101
|
-
const field = metadata[fieldName];
|
|
102
136
|
field.tag = tag;
|
|
103
137
|
if (!metadata[-2]) {
|
|
104
138
|
// -2: all field indexes with "view" tag
|
|
@@ -114,20 +148,14 @@ const Metadata = {
|
|
|
114
148
|
configurable: true
|
|
115
149
|
});
|
|
116
150
|
}
|
|
117
|
-
metadata[-2].push(
|
|
151
|
+
metadata[-2].push(index);
|
|
118
152
|
if (!metadata[-3][tag]) {
|
|
119
153
|
metadata[-3][tag] = [];
|
|
120
154
|
}
|
|
121
|
-
metadata[-3][tag].push(
|
|
155
|
+
metadata[-3][tag].push(index);
|
|
122
156
|
},
|
|
123
157
|
setFields(target, fields) {
|
|
124
158
|
const metadata = (target.prototype.constructor[Symbol.metadata] ??= {});
|
|
125
|
-
// target[$track] = function (changeTree, index: number, operation: OPERATION = OPERATION.ADD) {
|
|
126
|
-
// changeTree.change(index, operation, encodeSchemaOperation);
|
|
127
|
-
// };
|
|
128
|
-
// target[$encoder] = encodeSchemaOperation;
|
|
129
|
-
// target[$decoder] = decodeSchemaOperation;
|
|
130
|
-
// if (!target.prototype.toJSON) { target.prototype.toJSON = Schema.prototype.toJSON; }
|
|
131
159
|
let index = 0;
|
|
132
160
|
for (const field in fields) {
|
|
133
161
|
const type = fields[field];
|
|
@@ -135,7 +163,7 @@ const Metadata = {
|
|
|
135
163
|
const complexTypeKlass = (Array.isArray(type))
|
|
136
164
|
? getType("array")
|
|
137
165
|
: (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
|
|
138
|
-
Metadata.addField(metadata, index, field, type, getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass
|
|
166
|
+
Metadata.addField(metadata, index, field, type, getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass));
|
|
139
167
|
index++;
|
|
140
168
|
}
|
|
141
169
|
},
|
|
@@ -164,8 +192,9 @@ const Metadata = {
|
|
|
164
192
|
// assign parent metadata to current
|
|
165
193
|
Object.assign(metadata, parentMetadata);
|
|
166
194
|
for (let i = 0; i <= parentMetadata[-1]; i++) {
|
|
167
|
-
|
|
168
|
-
|
|
195
|
+
const fieldName = parentMetadata[i].name;
|
|
196
|
+
Object.defineProperty(metadata, fieldName, {
|
|
197
|
+
value: parentMetadata[fieldName],
|
|
169
198
|
enumerable: false,
|
|
170
199
|
configurable: true,
|
|
171
200
|
});
|
|
@@ -189,7 +218,7 @@ const Metadata = {
|
|
|
189
218
|
const metadata = klass[Symbol.metadata];
|
|
190
219
|
const fields = {};
|
|
191
220
|
for (let i = 0; i <= metadata[-1]; i++) {
|
|
192
|
-
fields[metadata[i]] = metadata[
|
|
221
|
+
fields[metadata[i].name] = metadata[i].type;
|
|
193
222
|
}
|
|
194
223
|
return fields;
|
|
195
224
|
}
|
|
@@ -198,48 +227,59 @@ const Metadata = {
|
|
|
198
227
|
var _a$5;
|
|
199
228
|
class ChangeTree {
|
|
200
229
|
static { _a$5 = $isNew; }
|
|
201
|
-
;
|
|
202
230
|
constructor(ref) {
|
|
203
|
-
this.
|
|
231
|
+
this.isFiltered = false;
|
|
232
|
+
this.isPartiallyFiltered = false;
|
|
204
233
|
this.currentOperationIndex = 0;
|
|
205
|
-
this.allChanges = new Map();
|
|
206
|
-
this.allFilteredChanges = new Map();
|
|
207
234
|
this.changes = new Map();
|
|
208
|
-
this.
|
|
235
|
+
this.allChanges = new Map();
|
|
209
236
|
this[_a$5] = true;
|
|
210
237
|
this.ref = ref;
|
|
238
|
+
//
|
|
239
|
+
// Does this structure have "filters" declared?
|
|
240
|
+
//
|
|
241
|
+
if (ref.constructor[Symbol.metadata]?.[-2]) {
|
|
242
|
+
this.allFilteredChanges = new Map();
|
|
243
|
+
this.filteredChanges = new Map();
|
|
244
|
+
}
|
|
211
245
|
}
|
|
212
246
|
setRoot(root) {
|
|
213
247
|
this.root = root;
|
|
214
248
|
this.root.add(this);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
249
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
250
|
+
if (this.root.types.hasFilters) {
|
|
251
|
+
//
|
|
252
|
+
// At Schema initialization, the "root" structure might not be available
|
|
253
|
+
// yet, as it only does once the "Encoder" has been set up.
|
|
254
|
+
//
|
|
255
|
+
// So the "parent" may be already set without a "root".
|
|
256
|
+
//
|
|
257
|
+
this.checkIsFiltered(metadata, this.parent, this.parentIndex);
|
|
258
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
259
|
+
this.root.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
260
|
+
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
224
263
|
if (!this.isFiltered) {
|
|
225
264
|
this.root.changes.set(this, this.changes);
|
|
265
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
226
266
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
267
|
+
this.ensureRefId();
|
|
268
|
+
if (metadata) {
|
|
269
|
+
metadata[-4]?.forEach((index) => {
|
|
270
|
+
const field = metadata[index];
|
|
271
|
+
const value = this.ref[field.name];
|
|
272
|
+
if (value) {
|
|
273
|
+
value[$changes].setRoot(root);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
230
276
|
}
|
|
231
|
-
if (
|
|
232
|
-
|
|
277
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
278
|
+
// MapSchema / ArraySchema, etc.
|
|
279
|
+
this.ref.forEach((value, key) => {
|
|
280
|
+
value[$changes].setRoot(root);
|
|
281
|
+
});
|
|
233
282
|
}
|
|
234
|
-
this.forEachChild((changeTree, _) => {
|
|
235
|
-
changeTree.setRoot(root);
|
|
236
|
-
});
|
|
237
|
-
// this.allChanges.forEach((_, index) => {
|
|
238
|
-
// const childRef = this.ref[$getByIndex](index);
|
|
239
|
-
// if (childRef && childRef[$changes]) {
|
|
240
|
-
// childRef[$changes].setRoot(root);
|
|
241
|
-
// }
|
|
242
|
-
// });
|
|
243
283
|
}
|
|
244
284
|
setParent(parent, root, parentIndex) {
|
|
245
285
|
this.parent = parent;
|
|
@@ -249,48 +289,60 @@ class ChangeTree {
|
|
|
249
289
|
return;
|
|
250
290
|
}
|
|
251
291
|
root.add(this);
|
|
292
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
252
293
|
// skip if parent is already set
|
|
253
|
-
if (root
|
|
254
|
-
this.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
294
|
+
if (root !== this.root) {
|
|
295
|
+
this.root = root;
|
|
296
|
+
if (root.types.hasFilters) {
|
|
297
|
+
this.checkIsFiltered(metadata, parent, parentIndex);
|
|
298
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
299
|
+
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
300
|
+
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (!this.isFiltered) {
|
|
304
|
+
this.root.changes.set(this, this.changes);
|
|
305
|
+
this.root.allChanges.set(this, this.allChanges);
|
|
306
|
+
}
|
|
307
|
+
this.ensureRefId();
|
|
258
308
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
309
|
+
// assign same parent on child structures
|
|
310
|
+
if (metadata) {
|
|
311
|
+
metadata[-4]?.forEach((index) => {
|
|
312
|
+
const field = metadata[index];
|
|
313
|
+
const value = this.ref[field.name];
|
|
314
|
+
value?.[$changes].setParent(this.ref, root, index);
|
|
315
|
+
// console.log(this.ref.constructor.name, field.name, value);
|
|
316
|
+
// try { throw new Error(); } catch (e) {
|
|
317
|
+
// console.log(e.stack);
|
|
318
|
+
// }
|
|
319
|
+
});
|
|
264
320
|
}
|
|
265
|
-
if (this.
|
|
266
|
-
|
|
267
|
-
this.
|
|
321
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
322
|
+
// MapSchema / ArraySchema, etc.
|
|
323
|
+
this.ref.forEach((value, key) => {
|
|
324
|
+
value[$changes].setParent(this.ref, root, this.indexes[key] ?? key);
|
|
325
|
+
});
|
|
268
326
|
}
|
|
269
|
-
this.ensureRefId();
|
|
270
|
-
this.forEachChild((changeTree, atIndex) => {
|
|
271
|
-
changeTree.setParent(this.ref, root, atIndex);
|
|
272
|
-
});
|
|
273
327
|
}
|
|
274
328
|
forEachChild(callback) {
|
|
275
329
|
//
|
|
276
330
|
// assign same parent on child structures
|
|
277
331
|
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const value = this.ref[field];
|
|
283
|
-
if (value
|
|
284
|
-
callback(value[$changes],
|
|
332
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
333
|
+
if (metadata) {
|
|
334
|
+
metadata[-4]?.forEach((index) => {
|
|
335
|
+
const field = metadata[index];
|
|
336
|
+
const value = this.ref[field.name];
|
|
337
|
+
if (value) {
|
|
338
|
+
callback(value[$changes], index);
|
|
285
339
|
}
|
|
286
|
-
}
|
|
340
|
+
});
|
|
287
341
|
}
|
|
288
|
-
else if (typeof (this.ref)
|
|
342
|
+
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
289
343
|
// MapSchema / ArraySchema, etc.
|
|
290
344
|
this.ref.forEach((value, key) => {
|
|
291
|
-
|
|
292
|
-
callback(value[$changes], this.ref[$changes].indexes[key] ?? key);
|
|
293
|
-
}
|
|
345
|
+
callback(value[$changes], this.indexes[key] ?? key);
|
|
294
346
|
});
|
|
295
347
|
}
|
|
296
348
|
}
|
|
@@ -299,8 +351,8 @@ class ChangeTree {
|
|
|
299
351
|
this.root?.changes.set(this, this.changes);
|
|
300
352
|
}
|
|
301
353
|
change(index, operation = exports.OPERATION.ADD) {
|
|
302
|
-
const metadata = this.ref
|
|
303
|
-
const isFiltered = this.isFiltered || (metadata
|
|
354
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
355
|
+
const isFiltered = this.isFiltered || (metadata?.[index]?.tag !== undefined);
|
|
304
356
|
const changeSet = (isFiltered)
|
|
305
357
|
? this.filteredChanges
|
|
306
358
|
: this.changes;
|
|
@@ -311,14 +363,14 @@ class ChangeTree {
|
|
|
311
363
|
: (previousOperation === exports.OPERATION.DELETE)
|
|
312
364
|
? exports.OPERATION.DELETE_AND_ADD
|
|
313
365
|
: operation;
|
|
366
|
+
//
|
|
367
|
+
// TODO: are DELETE operations being encoded as ADD here ??
|
|
368
|
+
//
|
|
314
369
|
changeSet.set(index, op);
|
|
315
370
|
}
|
|
316
|
-
//
|
|
317
|
-
// TODO: are DELETE operations being encoded as ADD here ??
|
|
318
|
-
//
|
|
319
371
|
if (isFiltered) {
|
|
320
|
-
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
321
372
|
this.allFilteredChanges.set(index, exports.OPERATION.ADD);
|
|
373
|
+
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
322
374
|
this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
|
|
323
375
|
}
|
|
324
376
|
else {
|
|
@@ -365,9 +417,7 @@ class ChangeTree {
|
|
|
365
417
|
});
|
|
366
418
|
}
|
|
367
419
|
indexedOperation(index, operation, allChangesIndex = index) {
|
|
368
|
-
|
|
369
|
-
const isFiltered = this.isFiltered || (metadata && metadata[metadata[index]].tag !== undefined);
|
|
370
|
-
if (isFiltered) {
|
|
420
|
+
if (this.filteredChanges !== undefined) {
|
|
371
421
|
this.allFilteredChanges.set(allChangesIndex, exports.OPERATION.ADD);
|
|
372
422
|
this.filteredChanges.set(index, operation);
|
|
373
423
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
@@ -380,8 +430,8 @@ class ChangeTree {
|
|
|
380
430
|
}
|
|
381
431
|
getType(index) {
|
|
382
432
|
if (Metadata.isValidInstance(this.ref)) {
|
|
383
|
-
const metadata = this.ref
|
|
384
|
-
return metadata[
|
|
433
|
+
const metadata = this.ref.constructor[Symbol.metadata];
|
|
434
|
+
return metadata[index].type;
|
|
385
435
|
}
|
|
386
436
|
else {
|
|
387
437
|
//
|
|
@@ -395,7 +445,7 @@ class ChangeTree {
|
|
|
395
445
|
}
|
|
396
446
|
getChange(index) {
|
|
397
447
|
// TODO: optimize this. avoid checking against multiple instances
|
|
398
|
-
return this.changes.get(index) ?? this.filteredChanges
|
|
448
|
+
return this.changes.get(index) ?? this.filteredChanges?.get(index);
|
|
399
449
|
}
|
|
400
450
|
//
|
|
401
451
|
// used during `.encode()`
|
|
@@ -416,16 +466,13 @@ class ChangeTree {
|
|
|
416
466
|
}
|
|
417
467
|
return;
|
|
418
468
|
}
|
|
419
|
-
const
|
|
420
|
-
const isFiltered = this.isFiltered || (metadata && metadata[metadata[index]].tag !== undefined);
|
|
421
|
-
const changeSet = (isFiltered)
|
|
469
|
+
const changeSet = (this.filteredChanges)
|
|
422
470
|
? this.filteredChanges
|
|
423
471
|
: this.changes;
|
|
424
472
|
const previousValue = this.getValue(index);
|
|
425
473
|
changeSet.set(index, operation ?? exports.OPERATION.DELETE);
|
|
426
474
|
// remove `root` reference
|
|
427
475
|
if (previousValue && previousValue[$changes]) {
|
|
428
|
-
previousValue[$changes].root = undefined;
|
|
429
476
|
//
|
|
430
477
|
// FIXME: this.root is "undefined"
|
|
431
478
|
//
|
|
@@ -436,12 +483,18 @@ class ChangeTree {
|
|
|
436
483
|
//
|
|
437
484
|
// (the property descriptors should NOT be used at decoding time. only at encoding time.)
|
|
438
485
|
//
|
|
439
|
-
this.root?.remove(previousValue[$changes]);
|
|
486
|
+
const refCount = this.root?.remove(previousValue[$changes]);
|
|
487
|
+
//
|
|
488
|
+
// Only remove "root" reference if it's the last reference
|
|
489
|
+
//
|
|
490
|
+
if (refCount <= 0) {
|
|
491
|
+
previousValue[$changes].root = undefined;
|
|
492
|
+
}
|
|
440
493
|
}
|
|
441
494
|
//
|
|
442
495
|
// FIXME: this is looking a bit ugly (and repeated from `.change()`)
|
|
443
496
|
//
|
|
444
|
-
if (
|
|
497
|
+
if (this.filteredChanges) {
|
|
445
498
|
this.root?.filteredChanges.set(this, this.filteredChanges);
|
|
446
499
|
this.allFilteredChanges.delete(allChangesIndex);
|
|
447
500
|
}
|
|
@@ -452,6 +505,7 @@ class ChangeTree {
|
|
|
452
505
|
}
|
|
453
506
|
endEncode() {
|
|
454
507
|
this.changes.clear();
|
|
508
|
+
// ArraySchema and MapSchema have a custom "encode end" method
|
|
455
509
|
this.ref[$onEncodeEnd]?.();
|
|
456
510
|
// Not a new instance anymore
|
|
457
511
|
delete this[$isNew];
|
|
@@ -464,12 +518,12 @@ class ChangeTree {
|
|
|
464
518
|
//
|
|
465
519
|
this.ref[$onEncodeEnd]?.();
|
|
466
520
|
this.changes.clear();
|
|
467
|
-
this.filteredChanges
|
|
521
|
+
this.filteredChanges?.clear();
|
|
468
522
|
// reset operation index
|
|
469
523
|
this.currentOperationIndex = 0;
|
|
470
524
|
if (discardAll) {
|
|
471
525
|
this.allChanges.clear();
|
|
472
|
-
this.allFilteredChanges
|
|
526
|
+
this.allFilteredChanges?.clear();
|
|
473
527
|
// remove children references
|
|
474
528
|
this.forEachChild((changeTree, _) => this.root?.remove(changeTree));
|
|
475
529
|
}
|
|
@@ -496,48 +550,41 @@ class ChangeTree {
|
|
|
496
550
|
get changed() {
|
|
497
551
|
return this.changes.size > 0;
|
|
498
552
|
}
|
|
499
|
-
checkIsFiltered(parent, parentIndex) {
|
|
553
|
+
checkIsFiltered(metadata, parent, parentIndex) {
|
|
500
554
|
// Detect if current structure has "filters" declared
|
|
501
|
-
this.isPartiallyFiltered =
|
|
502
|
-
if (
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const changes = this.changes;
|
|
535
|
-
this.changes = this.filteredChanges;
|
|
536
|
-
this.filteredChanges = changes;
|
|
537
|
-
// swap "all changes" reference
|
|
538
|
-
const allFilteredChanges = this.allFilteredChanges;
|
|
539
|
-
this.allFilteredChanges = this.allChanges;
|
|
540
|
-
this.allChanges = allFilteredChanges;
|
|
555
|
+
this.isPartiallyFiltered = metadata?.[-2] !== undefined;
|
|
556
|
+
if (this.isPartiallyFiltered) {
|
|
557
|
+
this.filteredChanges = this.filteredChanges || new Map();
|
|
558
|
+
this.allFilteredChanges = this.allFilteredChanges || new Map();
|
|
559
|
+
}
|
|
560
|
+
if (parent) {
|
|
561
|
+
if (!Metadata.isValidInstance(parent)) {
|
|
562
|
+
const parentChangeTree = parent[$changes];
|
|
563
|
+
parent = parentChangeTree.parent;
|
|
564
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
565
|
+
}
|
|
566
|
+
const parentMetadata = parent?.constructor?.[Symbol.metadata];
|
|
567
|
+
this.isFiltered = (parent && parentMetadata?.[-2]?.includes(parentIndex));
|
|
568
|
+
//
|
|
569
|
+
// TODO: refactor this!
|
|
570
|
+
//
|
|
571
|
+
// swapping `changes` and `filteredChanges` is required here
|
|
572
|
+
// because "isFiltered" may not be imedialely available on `change()`
|
|
573
|
+
//
|
|
574
|
+
if (this.isFiltered) {
|
|
575
|
+
this.filteredChanges = new Map();
|
|
576
|
+
this.allFilteredChanges = new Map();
|
|
577
|
+
if (this.changes.size > 0) {
|
|
578
|
+
// swap changes reference
|
|
579
|
+
const changes = this.changes;
|
|
580
|
+
this.changes = this.filteredChanges;
|
|
581
|
+
this.filteredChanges = changes;
|
|
582
|
+
// swap "all changes" reference
|
|
583
|
+
const allFilteredChanges = this.allFilteredChanges;
|
|
584
|
+
this.allFilteredChanges = this.allChanges;
|
|
585
|
+
this.allChanges = allFilteredChanges;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
541
588
|
}
|
|
542
589
|
}
|
|
543
590
|
}
|
|
@@ -821,62 +868,11 @@ var encode = /*#__PURE__*/Object.freeze({
|
|
|
821
868
|
writeFloat64: writeFloat64
|
|
822
869
|
});
|
|
823
870
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
let typeofTarget;
|
|
828
|
-
let allowNull = false;
|
|
829
|
-
switch (type) {
|
|
830
|
-
case "number":
|
|
831
|
-
case "int8":
|
|
832
|
-
case "uint8":
|
|
833
|
-
case "int16":
|
|
834
|
-
case "uint16":
|
|
835
|
-
case "int32":
|
|
836
|
-
case "uint32":
|
|
837
|
-
case "int64":
|
|
838
|
-
case "uint64":
|
|
839
|
-
case "float32":
|
|
840
|
-
case "float64":
|
|
841
|
-
typeofTarget = "number";
|
|
842
|
-
if (isNaN(value)) {
|
|
843
|
-
console.log(`trying to encode "NaN" in ${klass.constructor.name}#${field}`);
|
|
844
|
-
}
|
|
845
|
-
break;
|
|
846
|
-
case "string":
|
|
847
|
-
typeofTarget = "string";
|
|
848
|
-
allowNull = true;
|
|
849
|
-
break;
|
|
850
|
-
case "boolean":
|
|
851
|
-
// boolean is always encoded as true/false based on truthiness
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
854
|
-
if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) {
|
|
855
|
-
let foundValue = `'${JSON.stringify(value)}'${(value && value.constructor && ` (${value.constructor.name})`) || ''}`;
|
|
856
|
-
throw new EncodeSchemaError(`a '${typeofTarget}' was expected, but ${foundValue} was provided in ${klass.constructor.name}#${field}`);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
function assertInstanceType(value, type, klass, field) {
|
|
860
|
-
if (!(value instanceof type)) {
|
|
861
|
-
throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && value.constructor.name}' was provided in ${klass.constructor.name}#${field}`);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
function encodePrimitiveType(type, bytes, value, klass, field, it) {
|
|
866
|
-
assertType(value, type, klass, field);
|
|
867
|
-
const encodeFunc = encode[type];
|
|
868
|
-
if (encodeFunc) {
|
|
869
|
-
encodeFunc(bytes, value, it);
|
|
870
|
-
// encodeFunc(bytes, value);
|
|
871
|
-
}
|
|
872
|
-
else {
|
|
873
|
-
throw new EncodeSchemaError(`a '${type}' was expected, but ${value} was provided in ${klass.constructor.name}#${field}`);
|
|
871
|
+
function encodeValue(encoder, bytes, type, value, operation, it) {
|
|
872
|
+
if (typeof (type) === "string") {
|
|
873
|
+
encode[type]?.(bytes, value, it);
|
|
874
874
|
}
|
|
875
|
-
|
|
876
|
-
function encodeValue(encoder, bytes, ref, type, value, field, operation, it) {
|
|
877
|
-
if (type[Symbol.metadata] !== undefined) {
|
|
878
|
-
// TODO: move this to the `@type()` annotation
|
|
879
|
-
assertInstanceType(value, type, ref, field);
|
|
875
|
+
else if (type[Symbol.metadata] !== undefined) {
|
|
880
876
|
//
|
|
881
877
|
// Encode refId for this instance.
|
|
882
878
|
// The actual instance is going to be encoded on next `changeTree` iteration.
|
|
@@ -887,21 +883,7 @@ function encodeValue(encoder, bytes, ref, type, value, field, operation, it) {
|
|
|
887
883
|
encoder.tryEncodeTypeId(bytes, type, value.constructor, it);
|
|
888
884
|
}
|
|
889
885
|
}
|
|
890
|
-
else if (typeof (type) === "string") {
|
|
891
|
-
//
|
|
892
|
-
// Primitive values
|
|
893
|
-
//
|
|
894
|
-
encodePrimitiveType(type, bytes, value, ref, field, it);
|
|
895
|
-
}
|
|
896
886
|
else {
|
|
897
|
-
//
|
|
898
|
-
// Custom type (MapSchema, ArraySchema, etc)
|
|
899
|
-
//
|
|
900
|
-
const definition = getType(Object.keys(type)[0]);
|
|
901
|
-
//
|
|
902
|
-
// ensure a ArraySchema has been provided
|
|
903
|
-
//
|
|
904
|
-
assertInstanceType(ref[field], definition.constructor, ref, field);
|
|
905
887
|
//
|
|
906
888
|
// Encode refId for this instance.
|
|
907
889
|
// The actual instance is going to be encoded on next `changeTree` iteration.
|
|
@@ -914,26 +896,23 @@ function encodeValue(encoder, bytes, ref, type, value, field, operation, it) {
|
|
|
914
896
|
* @private
|
|
915
897
|
*/
|
|
916
898
|
const encodeSchemaOperation = function (encoder, bytes, changeTree, index, operation, it) {
|
|
917
|
-
const ref = changeTree.ref;
|
|
918
|
-
const metadata = ref['constructor'][Symbol.metadata];
|
|
919
|
-
const field = metadata[index];
|
|
920
|
-
const type = metadata[field].type;
|
|
921
|
-
const value = ref[field];
|
|
922
899
|
// "compress" field index + operation
|
|
923
900
|
bytes[it.offset++] = (index | operation) & 255;
|
|
924
901
|
// Do not encode value for DELETE operations
|
|
925
902
|
if (operation === exports.OPERATION.DELETE) {
|
|
926
903
|
return;
|
|
927
904
|
}
|
|
905
|
+
const ref = changeTree.ref;
|
|
906
|
+
const metadata = ref.constructor[Symbol.metadata];
|
|
907
|
+
const field = metadata[index];
|
|
928
908
|
// TODO: inline this function call small performance gain
|
|
929
|
-
encodeValue(encoder, bytes,
|
|
909
|
+
encodeValue(encoder, bytes, metadata[index].type, ref[field.name], operation, it);
|
|
930
910
|
};
|
|
931
911
|
/**
|
|
932
912
|
* Used for collections (MapSchema, CollectionSchema, SetSchema)
|
|
933
913
|
* @private
|
|
934
914
|
*/
|
|
935
|
-
const encodeKeyValueOperation = function (encoder, bytes, changeTree,
|
|
936
|
-
const ref = changeTree.ref;
|
|
915
|
+
const encodeKeyValueOperation = function (encoder, bytes, changeTree, index, operation, it) {
|
|
937
916
|
// encode operation
|
|
938
917
|
bytes[it.offset++] = operation & 255;
|
|
939
918
|
// custom operations
|
|
@@ -941,25 +920,26 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
941
920
|
return;
|
|
942
921
|
}
|
|
943
922
|
// encode index
|
|
944
|
-
number$1(bytes,
|
|
923
|
+
number$1(bytes, index, it);
|
|
945
924
|
// Do not encode value for DELETE operations
|
|
946
925
|
if (operation === exports.OPERATION.DELETE) {
|
|
947
926
|
return;
|
|
948
927
|
}
|
|
928
|
+
const ref = changeTree.ref;
|
|
949
929
|
//
|
|
950
930
|
// encode "alias" for dynamic fields (maps)
|
|
951
931
|
//
|
|
952
|
-
if ((operation & exports.OPERATION.ADD)
|
|
932
|
+
if ((operation & exports.OPERATION.ADD) === exports.OPERATION.ADD) { // ADD or DELETE_AND_ADD
|
|
953
933
|
if (typeof (ref['set']) === "function") {
|
|
954
934
|
//
|
|
955
935
|
// MapSchema dynamic key
|
|
956
936
|
//
|
|
957
|
-
const dynamicIndex = changeTree.ref['$indexes'].get(
|
|
937
|
+
const dynamicIndex = changeTree.ref['$indexes'].get(index);
|
|
958
938
|
string$1(bytes, dynamicIndex, it);
|
|
959
939
|
}
|
|
960
940
|
}
|
|
961
|
-
const type =
|
|
962
|
-
const value =
|
|
941
|
+
const type = ref[$childType];
|
|
942
|
+
const value = ref[$getByIndex](index);
|
|
963
943
|
// try { throw new Error(); } catch (e) {
|
|
964
944
|
// // only print if not coming from Reflection.ts
|
|
965
945
|
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
@@ -973,7 +953,7 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
|
|
|
973
953
|
// }
|
|
974
954
|
// }
|
|
975
955
|
// TODO: inline this function call small performance gain
|
|
976
|
-
encodeValue(encoder, bytes,
|
|
956
|
+
encodeValue(encoder, bytes, type, value, operation, it);
|
|
977
957
|
};
|
|
978
958
|
/**
|
|
979
959
|
* Used for collections (MapSchema, ArraySchema, etc.)
|
|
@@ -1017,7 +997,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
|
|
|
1017
997
|
// items: ref.toJSON(),
|
|
1018
998
|
// });
|
|
1019
999
|
// TODO: inline this function call small performance gain
|
|
1020
|
-
encodeValue(encoder, bytes,
|
|
1000
|
+
encodeValue(encoder, bytes, type, value, operation, it);
|
|
1021
1001
|
};
|
|
1022
1002
|
|
|
1023
1003
|
/**
|
|
@@ -1323,7 +1303,9 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1323
1303
|
if (!value) {
|
|
1324
1304
|
value = decoder.createInstanceOfType(childType);
|
|
1325
1305
|
}
|
|
1326
|
-
$root.addRef(refId, value, (value !== previousValue
|
|
1306
|
+
$root.addRef(refId, value, (value !== previousValue || // increment ref count if value has changed
|
|
1307
|
+
(operation === exports.OPERATION.DELETE_AND_ADD && value === previousValue) // increment ref count if the same instance is being added again
|
|
1308
|
+
));
|
|
1327
1309
|
}
|
|
1328
1310
|
}
|
|
1329
1311
|
else if (typeof (type) === "string") {
|
|
@@ -1374,7 +1356,7 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1374
1356
|
}
|
|
1375
1357
|
const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
1376
1358
|
const first_byte = bytes[it.offset++];
|
|
1377
|
-
const metadata = ref
|
|
1359
|
+
const metadata = ref.constructor[Symbol.metadata];
|
|
1378
1360
|
// "compressed" index + operation
|
|
1379
1361
|
const operation = (first_byte >> 6) << 6;
|
|
1380
1362
|
const index = first_byte % (operation || 255);
|
|
@@ -1384,9 +1366,9 @@ const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1384
1366
|
console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
|
|
1385
1367
|
return DEFINITION_MISMATCH;
|
|
1386
1368
|
}
|
|
1387
|
-
const { value, previousValue } = decodeValue(decoder, operation, ref, index,
|
|
1369
|
+
const { value, previousValue } = decodeValue(decoder, operation, ref, index, field.type, bytes, it, allChanges);
|
|
1388
1370
|
if (value !== null && value !== undefined) {
|
|
1389
|
-
ref[field] = value;
|
|
1371
|
+
ref[field.name] = value;
|
|
1390
1372
|
}
|
|
1391
1373
|
// add change
|
|
1392
1374
|
if (previousValue !== value) {
|
|
@@ -1394,7 +1376,7 @@ const decodeSchemaOperation = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1394
1376
|
ref,
|
|
1395
1377
|
refId: decoder.currentRefId,
|
|
1396
1378
|
op: operation,
|
|
1397
|
-
field: field,
|
|
1379
|
+
field: field.name,
|
|
1398
1380
|
value,
|
|
1399
1381
|
previousValue,
|
|
1400
1382
|
});
|
|
@@ -1492,7 +1474,6 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1492
1474
|
return;
|
|
1493
1475
|
}
|
|
1494
1476
|
else if (operation === exports.OPERATION.ADD_BY_REFID) {
|
|
1495
|
-
// operation = OPERATION.ADD;
|
|
1496
1477
|
const refId = number(bytes, it);
|
|
1497
1478
|
const itemByRefId = decoder.root.refs.get(refId);
|
|
1498
1479
|
// use existing index, or push new value
|
|
@@ -1526,6 +1507,47 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
|
|
|
1526
1507
|
}
|
|
1527
1508
|
};
|
|
1528
1509
|
|
|
1510
|
+
class EncodeSchemaError extends Error {
|
|
1511
|
+
}
|
|
1512
|
+
function assertType(value, type, klass, field) {
|
|
1513
|
+
let typeofTarget;
|
|
1514
|
+
let allowNull = false;
|
|
1515
|
+
switch (type) {
|
|
1516
|
+
case "number":
|
|
1517
|
+
case "int8":
|
|
1518
|
+
case "uint8":
|
|
1519
|
+
case "int16":
|
|
1520
|
+
case "uint16":
|
|
1521
|
+
case "int32":
|
|
1522
|
+
case "uint32":
|
|
1523
|
+
case "int64":
|
|
1524
|
+
case "uint64":
|
|
1525
|
+
case "float32":
|
|
1526
|
+
case "float64":
|
|
1527
|
+
typeofTarget = "number";
|
|
1528
|
+
if (isNaN(value)) {
|
|
1529
|
+
console.log(`trying to encode "NaN" in ${klass.constructor.name}#${field}`);
|
|
1530
|
+
}
|
|
1531
|
+
break;
|
|
1532
|
+
case "string":
|
|
1533
|
+
typeofTarget = "string";
|
|
1534
|
+
allowNull = true;
|
|
1535
|
+
break;
|
|
1536
|
+
case "boolean":
|
|
1537
|
+
// boolean is always encoded as true/false based on truthiness
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) {
|
|
1541
|
+
let foundValue = `'${JSON.stringify(value)}'${(value && value.constructor && ` (${value.constructor.name})`) || ''}`;
|
|
1542
|
+
throw new EncodeSchemaError(`a '${typeofTarget}' was expected, but ${foundValue} was provided in ${klass.constructor.name}#${field}`);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
function assertInstanceType(value, type, instance, field) {
|
|
1546
|
+
if (!(value instanceof type)) {
|
|
1547
|
+
throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && value.constructor.name}' was provided in ${instance.constructor.name}#${field}`);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1529
1551
|
var _a$4, _b$4;
|
|
1530
1552
|
const DEFAULT_SORT = (a, b) => {
|
|
1531
1553
|
const A = a.toString();
|
|
@@ -1591,6 +1613,7 @@ class ArraySchema {
|
|
|
1591
1613
|
}
|
|
1592
1614
|
else {
|
|
1593
1615
|
if (setValue[$changes]) {
|
|
1616
|
+
assertInstanceType(setValue, obj[$childType], obj, key);
|
|
1594
1617
|
if (obj.items[key] !== undefined) {
|
|
1595
1618
|
if (setValue[$changes][$isNew]) {
|
|
1596
1619
|
this[$changes].indexedOperation(Number(key), exports.OPERATION.MOVE_AND_ADD);
|
|
@@ -1637,6 +1660,7 @@ class ArraySchema {
|
|
|
1637
1660
|
}
|
|
1638
1661
|
});
|
|
1639
1662
|
this[$changes] = new ChangeTree(proxy);
|
|
1663
|
+
this[$changes].indexes = {};
|
|
1640
1664
|
this.push.apply(this, items);
|
|
1641
1665
|
return proxy;
|
|
1642
1666
|
}
|
|
@@ -1661,6 +1685,9 @@ class ArraySchema {
|
|
|
1661
1685
|
if (value === undefined || value === null) {
|
|
1662
1686
|
return;
|
|
1663
1687
|
}
|
|
1688
|
+
else if (typeof (value) === "object" && this[$childType]) {
|
|
1689
|
+
assertInstanceType(value, this[$childType], this, i);
|
|
1690
|
+
}
|
|
1664
1691
|
const changeTree = this[$changes];
|
|
1665
1692
|
changeTree.indexedOperation(length, exports.OPERATION.ADD, this.items.length);
|
|
1666
1693
|
this.items.push(value);
|
|
@@ -2188,6 +2215,7 @@ class MapSchema {
|
|
|
2188
2215
|
this.$items = new Map();
|
|
2189
2216
|
this.$indexes = new Map();
|
|
2190
2217
|
this[$changes] = new ChangeTree(this);
|
|
2218
|
+
this[$changes].indexes = {};
|
|
2191
2219
|
if (initialValues) {
|
|
2192
2220
|
if (initialValues instanceof Map ||
|
|
2193
2221
|
initialValues instanceof MapSchema) {
|
|
@@ -2214,6 +2242,9 @@ class MapSchema {
|
|
|
2214
2242
|
if (value === undefined || value === null) {
|
|
2215
2243
|
throw new Error(`MapSchema#set('${key}', ${value}): trying to set ${value} value on '${key}'.`);
|
|
2216
2244
|
}
|
|
2245
|
+
else if (typeof (value) === "object" && this[$childType]) {
|
|
2246
|
+
assertInstanceType(value, this[$childType], this, key);
|
|
2247
|
+
}
|
|
2217
2248
|
// Force "key" as string
|
|
2218
2249
|
// See: https://github.com/colyseus/colyseus/issues/561#issuecomment-1646733468
|
|
2219
2250
|
key = key.toString();
|
|
@@ -2419,27 +2450,20 @@ class TypeContext {
|
|
|
2419
2450
|
if (parentFieldViewTag !== undefined) {
|
|
2420
2451
|
this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
|
|
2421
2452
|
}
|
|
2422
|
-
for (const
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
// if (
|
|
2427
|
-
// parentFieldViewTag !== undefined &&
|
|
2428
|
-
// metadata[field].tag === undefined
|
|
2429
|
-
// ) {
|
|
2430
|
-
// metadata[field].tag = parentFieldViewTag;
|
|
2431
|
-
// }
|
|
2432
|
-
const fieldType = metadata[field].type;
|
|
2433
|
-
const viewTag = metadata[field].tag;
|
|
2453
|
+
for (const fieldIndex in metadata) {
|
|
2454
|
+
const index = fieldIndex;
|
|
2455
|
+
const fieldType = metadata[index].type;
|
|
2456
|
+
const viewTag = metadata[index].tag;
|
|
2434
2457
|
if (typeof (fieldType) === "string") {
|
|
2435
2458
|
continue;
|
|
2436
2459
|
}
|
|
2437
2460
|
if (Array.isArray(fieldType)) {
|
|
2438
2461
|
const type = fieldType[0];
|
|
2462
|
+
// skip primitive types
|
|
2439
2463
|
if (type === "string") {
|
|
2440
2464
|
continue;
|
|
2441
2465
|
}
|
|
2442
|
-
this.discoverTypes(type,
|
|
2466
|
+
this.discoverTypes(type, index, viewTag);
|
|
2443
2467
|
}
|
|
2444
2468
|
else if (typeof (fieldType) === "function") {
|
|
2445
2469
|
this.discoverTypes(fieldType, viewTag);
|
|
@@ -2450,7 +2474,7 @@ class TypeContext {
|
|
|
2450
2474
|
if (typeof (type) === "string") {
|
|
2451
2475
|
continue;
|
|
2452
2476
|
}
|
|
2453
|
-
this.discoverTypes(type,
|
|
2477
|
+
this.discoverTypes(type, index, viewTag);
|
|
2454
2478
|
}
|
|
2455
2479
|
}
|
|
2456
2480
|
}
|
|
@@ -2606,17 +2630,18 @@ function view(tag = DEFAULT_VIEW_TAG) {
|
|
|
2606
2630
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2607
2631
|
// TODO: use Metadata.initialize()
|
|
2608
2632
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
}
|
|
2633
|
+
// const fieldIndex = metadata[fieldName];
|
|
2634
|
+
// if (!metadata[fieldIndex]) {
|
|
2635
|
+
// //
|
|
2636
|
+
// // detect index for this field, considering inheritance
|
|
2637
|
+
// //
|
|
2638
|
+
// metadata[fieldIndex] = {
|
|
2639
|
+
// type: undefined,
|
|
2640
|
+
// index: (metadata[-1] // current structure already has fields defined
|
|
2641
|
+
// ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
|
|
2642
|
+
// ?? -1) + 1 // no fields defined
|
|
2643
|
+
// }
|
|
2644
|
+
// }
|
|
2620
2645
|
Metadata.setTag(metadata, fieldName, tag);
|
|
2621
2646
|
};
|
|
2622
2647
|
}
|
|
@@ -2631,16 +2656,16 @@ function type(type, options) {
|
|
|
2631
2656
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2632
2657
|
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
2633
2658
|
const metadata = Metadata.initialize(constructor, parentMetadata);
|
|
2634
|
-
let fieldIndex;
|
|
2659
|
+
let fieldIndex = metadata[field];
|
|
2635
2660
|
/**
|
|
2636
2661
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
2637
2662
|
*/
|
|
2638
|
-
if (metadata[
|
|
2639
|
-
if (metadata[
|
|
2663
|
+
if (metadata[fieldIndex]) {
|
|
2664
|
+
if (metadata[fieldIndex].deprecated) {
|
|
2640
2665
|
// do not create accessors for deprecated properties.
|
|
2641
2666
|
return;
|
|
2642
2667
|
}
|
|
2643
|
-
else if (metadata[
|
|
2668
|
+
else if (metadata[fieldIndex].type !== undefined) {
|
|
2644
2669
|
// trying to define same property multiple times across inheritance.
|
|
2645
2670
|
// https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
|
|
2646
2671
|
try {
|
|
@@ -2651,9 +2676,6 @@ function type(type, options) {
|
|
|
2651
2676
|
throw new Error(`${e.message} ${definitionAtLine}`);
|
|
2652
2677
|
}
|
|
2653
2678
|
}
|
|
2654
|
-
else {
|
|
2655
|
-
fieldIndex = metadata[field].index;
|
|
2656
|
-
}
|
|
2657
2679
|
}
|
|
2658
2680
|
else {
|
|
2659
2681
|
//
|
|
@@ -2679,11 +2701,11 @@ function type(type, options) {
|
|
|
2679
2701
|
const childType = (complexTypeKlass)
|
|
2680
2702
|
? Object.values(type)[0]
|
|
2681
2703
|
: type;
|
|
2682
|
-
Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass
|
|
2704
|
+
Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass));
|
|
2683
2705
|
}
|
|
2684
2706
|
};
|
|
2685
2707
|
}
|
|
2686
|
-
function getPropertyDescriptor(fieldCached, fieldIndex, type, complexTypeKlass
|
|
2708
|
+
function getPropertyDescriptor(fieldCached, fieldIndex, type, complexTypeKlass) {
|
|
2687
2709
|
return {
|
|
2688
2710
|
get: function () { return this[fieldCached]; },
|
|
2689
2711
|
set: function (value) {
|
|
@@ -2705,22 +2727,27 @@ function getPropertyDescriptor(fieldCached, fieldIndex, type, complexTypeKlass,
|
|
|
2705
2727
|
}
|
|
2706
2728
|
value[$childType] = type;
|
|
2707
2729
|
}
|
|
2730
|
+
else if (typeof (type) !== "string") {
|
|
2731
|
+
assertInstanceType(value, type, this, fieldCached.substring(1));
|
|
2732
|
+
}
|
|
2733
|
+
else {
|
|
2734
|
+
assertType(value, type, this, fieldCached.substring(1));
|
|
2735
|
+
}
|
|
2736
|
+
const changeTree = this[$changes];
|
|
2708
2737
|
//
|
|
2709
2738
|
// Replacing existing "ref", remove it from root.
|
|
2710
2739
|
// TODO: if there are other references to this instance, we should not remove it from root.
|
|
2711
2740
|
//
|
|
2712
2741
|
if (previousValue !== undefined && previousValue[$changes]) {
|
|
2713
|
-
|
|
2742
|
+
changeTree.root?.remove(previousValue[$changes]);
|
|
2714
2743
|
}
|
|
2715
2744
|
// flag the change for encoding.
|
|
2716
|
-
this.constructor[$track](
|
|
2745
|
+
this.constructor[$track](changeTree, fieldIndex, exports.OPERATION.ADD);
|
|
2717
2746
|
//
|
|
2718
2747
|
// call setParent() recursively for this and its child
|
|
2719
2748
|
// structures.
|
|
2720
2749
|
//
|
|
2721
|
-
|
|
2722
|
-
value[$changes].setParent(this, this[$changes].root, metadata[field].index);
|
|
2723
|
-
}
|
|
2750
|
+
value[$changes]?.setParent(this, changeTree.root, fieldIndex);
|
|
2724
2751
|
}
|
|
2725
2752
|
else if (previousValue !== undefined) {
|
|
2726
2753
|
//
|
|
@@ -2747,20 +2774,22 @@ function deprecated(throws = true) {
|
|
|
2747
2774
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
2748
2775
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
2749
2776
|
const metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
}
|
|
2761
|
-
|
|
2777
|
+
const fieldIndex = metadata[field];
|
|
2778
|
+
// if (!metadata[field]) {
|
|
2779
|
+
// //
|
|
2780
|
+
// // detect index for this field, considering inheritance
|
|
2781
|
+
// //
|
|
2782
|
+
// metadata[field] = {
|
|
2783
|
+
// type: undefined,
|
|
2784
|
+
// index: (metadata[-1] // current structure already has fields defined
|
|
2785
|
+
// ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
|
|
2786
|
+
// ?? -1) + 1 // no fields defined
|
|
2787
|
+
// }
|
|
2788
|
+
// }
|
|
2789
|
+
metadata[fieldIndex].deprecated = true;
|
|
2762
2790
|
if (throws) {
|
|
2763
|
-
metadata[
|
|
2791
|
+
metadata[$descriptors] ??= {};
|
|
2792
|
+
metadata[$descriptors][field] = {
|
|
2764
2793
|
get: function () { throw new Error(`${field} is deprecated.`); },
|
|
2765
2794
|
set: function (value) { },
|
|
2766
2795
|
enumerable: false,
|
|
@@ -2768,8 +2797,8 @@ function deprecated(throws = true) {
|
|
|
2768
2797
|
};
|
|
2769
2798
|
}
|
|
2770
2799
|
// flag metadata[field] as non-enumerable
|
|
2771
|
-
Object.defineProperty(metadata,
|
|
2772
|
-
value: metadata[
|
|
2800
|
+
Object.defineProperty(metadata, fieldIndex, {
|
|
2801
|
+
value: metadata[fieldIndex],
|
|
2773
2802
|
enumerable: false,
|
|
2774
2803
|
configurable: true
|
|
2775
2804
|
});
|
|
@@ -2835,35 +2864,7 @@ class Schema {
|
|
|
2835
2864
|
enumerable: false,
|
|
2836
2865
|
writable: true
|
|
2837
2866
|
});
|
|
2838
|
-
|
|
2839
|
-
// Define property descriptors
|
|
2840
|
-
for (const field in metadata) {
|
|
2841
|
-
if (metadata[field].descriptor) {
|
|
2842
|
-
// for encoder
|
|
2843
|
-
Object.defineProperty(instance, `_${field}`, {
|
|
2844
|
-
value: undefined,
|
|
2845
|
-
writable: true,
|
|
2846
|
-
enumerable: false,
|
|
2847
|
-
configurable: true,
|
|
2848
|
-
});
|
|
2849
|
-
Object.defineProperty(instance, field, metadata[field].descriptor);
|
|
2850
|
-
}
|
|
2851
|
-
else {
|
|
2852
|
-
// for decoder
|
|
2853
|
-
Object.defineProperty(instance, field, {
|
|
2854
|
-
value: undefined,
|
|
2855
|
-
writable: true,
|
|
2856
|
-
enumerable: true,
|
|
2857
|
-
configurable: true,
|
|
2858
|
-
});
|
|
2859
|
-
}
|
|
2860
|
-
// Object.defineProperty(instance, field, {
|
|
2861
|
-
// ...instance.constructor[Symbol.metadata][field].descriptor
|
|
2862
|
-
// });
|
|
2863
|
-
// if (args[0]?.hasOwnProperty(field)) {
|
|
2864
|
-
// instance[field] = args[0][field];
|
|
2865
|
-
// }
|
|
2866
|
-
}
|
|
2867
|
+
Object.defineProperties(instance, instance.constructor[Symbol.metadata]?.[$descriptors] || {});
|
|
2867
2868
|
}
|
|
2868
2869
|
static is(type) {
|
|
2869
2870
|
return typeof (type[Symbol.metadata]) === "object";
|
|
@@ -2887,7 +2888,7 @@ class Schema {
|
|
|
2887
2888
|
*/
|
|
2888
2889
|
static [$filter](ref, index, view) {
|
|
2889
2890
|
const metadata = ref.constructor[Symbol.metadata];
|
|
2890
|
-
const tag = metadata[
|
|
2891
|
+
const tag = metadata[index]?.tag;
|
|
2891
2892
|
if (view === undefined) {
|
|
2892
2893
|
// shared pass/encode: encode if doesn't have a tag
|
|
2893
2894
|
return tag === undefined;
|
|
@@ -2908,12 +2909,21 @@ class Schema {
|
|
|
2908
2909
|
}
|
|
2909
2910
|
// allow inherited classes to have a constructor
|
|
2910
2911
|
constructor(...args) {
|
|
2911
|
-
|
|
2912
|
+
//
|
|
2913
|
+
// inline
|
|
2914
|
+
// Schema.initialize(this);
|
|
2915
|
+
//
|
|
2916
|
+
Object.defineProperty(this, $changes, {
|
|
2917
|
+
value: new ChangeTree(this),
|
|
2918
|
+
enumerable: false,
|
|
2919
|
+
writable: true
|
|
2920
|
+
});
|
|
2921
|
+
Object.defineProperties(this, this.constructor[Symbol.metadata]?.[$descriptors] || {});
|
|
2912
2922
|
//
|
|
2913
2923
|
// Assign initial values
|
|
2914
2924
|
//
|
|
2915
2925
|
if (args[0]) {
|
|
2916
|
-
|
|
2926
|
+
Object.assign(this, args[0]);
|
|
2917
2927
|
}
|
|
2918
2928
|
}
|
|
2919
2929
|
assign(props) {
|
|
@@ -2927,7 +2937,8 @@ class Schema {
|
|
|
2927
2937
|
* @param operation OPERATION to perform (detected automatically)
|
|
2928
2938
|
*/
|
|
2929
2939
|
setDirty(property, operation) {
|
|
2930
|
-
this
|
|
2940
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
2941
|
+
this[$changes].change(metadata[metadata[property]].index, operation);
|
|
2931
2942
|
}
|
|
2932
2943
|
clone() {
|
|
2933
2944
|
const cloned = new (this.constructor);
|
|
@@ -2936,7 +2947,9 @@ class Schema {
|
|
|
2936
2947
|
// TODO: clone all properties, not only annotated ones
|
|
2937
2948
|
//
|
|
2938
2949
|
// for (const field in this) {
|
|
2939
|
-
for (const
|
|
2950
|
+
for (const fieldIndex in metadata) {
|
|
2951
|
+
// const field = metadata[metadata[fieldIndex]].name;
|
|
2952
|
+
const field = metadata[fieldIndex].name;
|
|
2940
2953
|
if (typeof (this[field]) === "object" &&
|
|
2941
2954
|
typeof (this[field]?.clone) === "function") {
|
|
2942
2955
|
// deep clone
|
|
@@ -2950,10 +2963,11 @@ class Schema {
|
|
|
2950
2963
|
return cloned;
|
|
2951
2964
|
}
|
|
2952
2965
|
toJSON() {
|
|
2953
|
-
const metadata = this.constructor[Symbol.metadata];
|
|
2954
2966
|
const obj = {};
|
|
2955
|
-
|
|
2956
|
-
|
|
2967
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
2968
|
+
for (const index in metadata) {
|
|
2969
|
+
const field = metadata[index];
|
|
2970
|
+
const fieldName = field.name;
|
|
2957
2971
|
if (!field.deprecated && this[fieldName] !== null && typeof (this[fieldName]) !== "undefined") {
|
|
2958
2972
|
obj[fieldName] = (typeof (this[fieldName]['toJSON']) === "function")
|
|
2959
2973
|
? this[fieldName]['toJSON']()
|
|
@@ -2966,10 +2980,12 @@ class Schema {
|
|
|
2966
2980
|
this[$changes].discardAll();
|
|
2967
2981
|
}
|
|
2968
2982
|
[$getByIndex](index) {
|
|
2969
|
-
|
|
2983
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
2984
|
+
return this[metadata[index].name];
|
|
2970
2985
|
}
|
|
2971
2986
|
[$deleteByIndex](index) {
|
|
2972
|
-
this
|
|
2987
|
+
const metadata = this.constructor[Symbol.metadata];
|
|
2988
|
+
this[metadata[index].name] = undefined;
|
|
2973
2989
|
}
|
|
2974
2990
|
static debugRefIds(instance, jsonContents = true, level = 0) {
|
|
2975
2991
|
const ref = instance;
|
|
@@ -3093,6 +3109,7 @@ class CollectionSchema {
|
|
|
3093
3109
|
this.$indexes = new Map();
|
|
3094
3110
|
this.$refId = 0;
|
|
3095
3111
|
this[$changes] = new ChangeTree(this);
|
|
3112
|
+
this[$changes].indexes = {};
|
|
3096
3113
|
if (initialValues) {
|
|
3097
3114
|
initialValues.forEach((v) => this.add(v));
|
|
3098
3115
|
}
|
|
@@ -3248,6 +3265,7 @@ class SetSchema {
|
|
|
3248
3265
|
this.$indexes = new Map();
|
|
3249
3266
|
this.$refId = 0;
|
|
3250
3267
|
this[$changes] = new ChangeTree(this);
|
|
3268
|
+
this[$changes].indexes = {};
|
|
3251
3269
|
if (initialValues) {
|
|
3252
3270
|
initialValues.forEach((v) => this.add(v));
|
|
3253
3271
|
}
|
|
@@ -3434,24 +3452,36 @@ class Root {
|
|
|
3434
3452
|
return this.nextUniqueId++;
|
|
3435
3453
|
}
|
|
3436
3454
|
add(changeTree) {
|
|
3437
|
-
const
|
|
3438
|
-
|
|
3455
|
+
const previousRefCount = this.refCount.get(changeTree);
|
|
3456
|
+
if (previousRefCount === 0) {
|
|
3457
|
+
//
|
|
3458
|
+
// When a ChangeTree is re-added, it means that it was previously removed.
|
|
3459
|
+
// We need to re-add all changes to the `changes` map.
|
|
3460
|
+
//
|
|
3461
|
+
changeTree.allChanges.forEach((operation, index) => {
|
|
3462
|
+
changeTree.changes.set(index, operation);
|
|
3463
|
+
});
|
|
3464
|
+
}
|
|
3465
|
+
const refCount = (previousRefCount || 0) + 1;
|
|
3466
|
+
this.refCount.set(changeTree, refCount);
|
|
3467
|
+
return refCount;
|
|
3439
3468
|
}
|
|
3440
3469
|
remove(changeTree) {
|
|
3441
|
-
const refCount = this.refCount.get(changeTree);
|
|
3442
|
-
if (refCount <=
|
|
3470
|
+
const refCount = (this.refCount.get(changeTree)) - 1;
|
|
3471
|
+
if (refCount <= 0) {
|
|
3443
3472
|
this.allChanges.delete(changeTree);
|
|
3444
3473
|
this.changes.delete(changeTree);
|
|
3445
3474
|
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
3446
3475
|
this.allFilteredChanges.delete(changeTree);
|
|
3447
3476
|
this.filteredChanges.delete(changeTree);
|
|
3448
3477
|
}
|
|
3449
|
-
this.refCount.
|
|
3478
|
+
this.refCount.set(changeTree, 0);
|
|
3450
3479
|
}
|
|
3451
3480
|
else {
|
|
3452
|
-
this.refCount.set(changeTree, refCount
|
|
3481
|
+
this.refCount.set(changeTree, refCount);
|
|
3453
3482
|
}
|
|
3454
3483
|
changeTree.forEachChild((child, _) => this.remove(child));
|
|
3484
|
+
return refCount;
|
|
3455
3485
|
}
|
|
3456
3486
|
clear() {
|
|
3457
3487
|
this.changes.clear();
|
|
@@ -3482,10 +3512,10 @@ class Encoder {
|
|
|
3482
3512
|
) {
|
|
3483
3513
|
const hasView = (view !== undefined);
|
|
3484
3514
|
const rootChangeTree = this.state[$changes];
|
|
3485
|
-
const
|
|
3486
|
-
for (const [changeTree, changes] of
|
|
3515
|
+
const shouldClearChanges = !isEncodeAll && !hasView;
|
|
3516
|
+
for (const [changeTree, changes] of changeTrees.entries()) {
|
|
3487
3517
|
const ref = changeTree.ref;
|
|
3488
|
-
const ctor = ref
|
|
3518
|
+
const ctor = ref.constructor;
|
|
3489
3519
|
const encoder = ctor[$encoder];
|
|
3490
3520
|
const filter = ctor[$filter];
|
|
3491
3521
|
// try { throw new Error(); } catch (e) {
|
|
@@ -3509,8 +3539,7 @@ class Encoder {
|
|
|
3509
3539
|
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3510
3540
|
number$1(buffer, changeTree.refId, it);
|
|
3511
3541
|
}
|
|
3512
|
-
const
|
|
3513
|
-
for (const [fieldIndex, operation] of changesIterator) {
|
|
3542
|
+
for (const [fieldIndex, operation] of changes.entries()) {
|
|
3514
3543
|
//
|
|
3515
3544
|
// first pass (encodeAll), identify "filtered" operations without encoding them
|
|
3516
3545
|
// they will be encoded per client, based on their view.
|
|
@@ -3535,6 +3564,14 @@ class Encoder {
|
|
|
3535
3564
|
// }
|
|
3536
3565
|
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
3537
3566
|
}
|
|
3567
|
+
// if (shouldClearChanges) {
|
|
3568
|
+
// // changeTree.endEncode();
|
|
3569
|
+
// changeTree.changes.clear();
|
|
3570
|
+
// // ArraySchema and MapSchema have a custom "encode end" method
|
|
3571
|
+
// changeTree.ref[$onEncodeEnd]?.();
|
|
3572
|
+
// // Not a new instance anymore
|
|
3573
|
+
// delete changeTree[$isNew];
|
|
3574
|
+
// }
|
|
3538
3575
|
}
|
|
3539
3576
|
if (it.offset > buffer.byteLength) {
|
|
3540
3577
|
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
@@ -3557,7 +3594,7 @@ class Encoder {
|
|
|
3557
3594
|
//
|
|
3558
3595
|
// only clear changes after making sure buffer resize is not required.
|
|
3559
3596
|
//
|
|
3560
|
-
if (
|
|
3597
|
+
if (shouldClearChanges) {
|
|
3561
3598
|
//
|
|
3562
3599
|
// FIXME: avoid iterating over change trees twice.
|
|
3563
3600
|
//
|
|
@@ -3641,6 +3678,11 @@ class Encoder {
|
|
|
3641
3678
|
const changeTreesIterator = changeTrees.entries();
|
|
3642
3679
|
for (const [changeTree, _] of changeTreesIterator) {
|
|
3643
3680
|
changeTree.endEncode();
|
|
3681
|
+
// changeTree.changes.clear();
|
|
3682
|
+
// // ArraySchema and MapSchema have a custom "encode end" method
|
|
3683
|
+
// changeTree.ref[$onEncodeEnd]?.();
|
|
3684
|
+
// // Not a new instance anymore
|
|
3685
|
+
// delete changeTree[$isNew];
|
|
3644
3686
|
}
|
|
3645
3687
|
}
|
|
3646
3688
|
discardChanges() {
|
|
@@ -3756,8 +3798,9 @@ class ReferenceTracker {
|
|
|
3756
3798
|
// Ensure child schema instances have their references removed as well.
|
|
3757
3799
|
//
|
|
3758
3800
|
if (Metadata.isValidInstance(ref)) {
|
|
3759
|
-
const metadata = ref
|
|
3760
|
-
for (const
|
|
3801
|
+
const metadata = ref.constructor[Symbol.metadata];
|
|
3802
|
+
for (const index in metadata) {
|
|
3803
|
+
const field = metadata[index].name;
|
|
3761
3804
|
const childRefId = typeof (ref[field]) === "object" && this.refIds.get(ref[field]);
|
|
3762
3805
|
if (childRefId) {
|
|
3763
3806
|
this.removeRef(childRefId);
|
|
@@ -3944,7 +3987,9 @@ class Reflection extends Schema {
|
|
|
3944
3987
|
const reflection = new Reflection();
|
|
3945
3988
|
const encoder = new Encoder(reflection);
|
|
3946
3989
|
const buildType = (currentType, metadata) => {
|
|
3947
|
-
for (const
|
|
3990
|
+
for (const fieldIndex in metadata) {
|
|
3991
|
+
const index = Number(fieldIndex);
|
|
3992
|
+
const fieldName = metadata[index].name;
|
|
3948
3993
|
// skip fields from parent classes
|
|
3949
3994
|
if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
|
|
3950
3995
|
continue;
|
|
@@ -3952,7 +3997,7 @@ class Reflection extends Schema {
|
|
|
3952
3997
|
const field = new ReflectionField();
|
|
3953
3998
|
field.name = fieldName;
|
|
3954
3999
|
let fieldType;
|
|
3955
|
-
const type = metadata[
|
|
4000
|
+
const type = metadata[index].type;
|
|
3956
4001
|
if (typeof (type) === "string") {
|
|
3957
4002
|
fieldType = type;
|
|
3958
4003
|
}
|
|
@@ -4018,18 +4063,7 @@ class Reflection extends Schema {
|
|
|
4018
4063
|
reflection.types.forEach((reflectionType) => {
|
|
4019
4064
|
const schemaType = typeContext.get(reflectionType.id);
|
|
4020
4065
|
const metadata = schemaType[Symbol.metadata];
|
|
4021
|
-
// FIXME: use metadata[-1] to get field count
|
|
4022
4066
|
const parentFieldIndex = 0;
|
|
4023
|
-
// console.log("--------------------");
|
|
4024
|
-
// // console.log("reflectionType", reflectionType.toJSON());
|
|
4025
|
-
// console.log("reflectionType.fields", reflectionType.fields.toJSON());
|
|
4026
|
-
// console.log("parentFieldIndex", parentFieldIndex);
|
|
4027
|
-
//
|
|
4028
|
-
// FIXME: set fields using parentKlass as well
|
|
4029
|
-
// currently the fields are duplicated on inherited classes
|
|
4030
|
-
//
|
|
4031
|
-
// // const parentKlass = reflection.types[reflectionType.extendsId];
|
|
4032
|
-
// // parentKlass.fields
|
|
4033
4067
|
reflectionType.fields.forEach((field, i) => {
|
|
4034
4068
|
const fieldIndex = parentFieldIndex + i;
|
|
4035
4069
|
if (field.referencedType !== undefined) {
|
|
@@ -4194,7 +4228,7 @@ function getDecoderStateCallbacks(decoder) {
|
|
|
4194
4228
|
//
|
|
4195
4229
|
bindTo: function bindTo(targetObject, properties) {
|
|
4196
4230
|
if (!properties) {
|
|
4197
|
-
properties = Object.keys(metadata);
|
|
4231
|
+
properties = Object.keys(metadata).map((index) => metadata[index].name);
|
|
4198
4232
|
}
|
|
4199
4233
|
return $root.addCallback($root.refIds.get(context.instance), exports.OPERATION.REPLACE, () => {
|
|
4200
4234
|
properties.forEach((prop) => targetObject[prop] = context.instance[prop]);
|
|
@@ -4202,7 +4236,8 @@ function getDecoderStateCallbacks(decoder) {
|
|
|
4202
4236
|
}
|
|
4203
4237
|
}, {
|
|
4204
4238
|
get(target, prop) {
|
|
4205
|
-
|
|
4239
|
+
const metadataField = metadata[metadata[prop]];
|
|
4240
|
+
if (metadataField) {
|
|
4206
4241
|
const instance = context.instance?.[prop];
|
|
4207
4242
|
const onInstanceAvailable = ((callback) => {
|
|
4208
4243
|
const unbind = $(context.instance).listen(prop, (value, _) => {
|
|
@@ -4218,7 +4253,7 @@ function getDecoderStateCallbacks(decoder) {
|
|
|
4218
4253
|
callback(instance, true);
|
|
4219
4254
|
}
|
|
4220
4255
|
});
|
|
4221
|
-
return getProxy(
|
|
4256
|
+
return getProxy(metadataField.type, {
|
|
4222
4257
|
// make sure refId is available, otherwise need to wait for the instance to be available.
|
|
4223
4258
|
instance: ($root.refIds.get(instance) && instance),
|
|
4224
4259
|
parentInstance: context.instance,
|
|
@@ -4331,7 +4366,7 @@ class StateView {
|
|
|
4331
4366
|
console.warn("StateView#add(), invalid object:", obj);
|
|
4332
4367
|
return this;
|
|
4333
4368
|
}
|
|
4334
|
-
// FIXME: ArraySchema/MapSchema
|
|
4369
|
+
// FIXME: ArraySchema/MapSchema do not have metadata
|
|
4335
4370
|
const metadata = obj.constructor[Symbol.metadata];
|
|
4336
4371
|
const changeTree = obj[$changes];
|
|
4337
4372
|
this.items.add(changeTree);
|
|
@@ -4377,7 +4412,7 @@ class StateView {
|
|
|
4377
4412
|
? changeTree.allFilteredChanges
|
|
4378
4413
|
: changeTree.allChanges;
|
|
4379
4414
|
changeSet.forEach((op, index) => {
|
|
4380
|
-
const tagAtIndex = metadata?.[
|
|
4415
|
+
const tagAtIndex = metadata?.[index].tag;
|
|
4381
4416
|
if ((isInvisible || // if "invisible", include all
|
|
4382
4417
|
tagAtIndex === undefined || // "all change" with no tag
|
|
4383
4418
|
tagAtIndex === tag // tagged property
|
|
@@ -4390,7 +4425,7 @@ class StateView {
|
|
|
4390
4425
|
// Add children of this ChangeTree to this view
|
|
4391
4426
|
changeTree.forEachChild((change, index) => {
|
|
4392
4427
|
// Do not ADD children that don't have the same tag
|
|
4393
|
-
if (metadata && metadata[
|
|
4428
|
+
if (metadata && metadata[index].tag !== tag) {
|
|
4394
4429
|
return;
|
|
4395
4430
|
}
|
|
4396
4431
|
this.add(change.ref, tag, false);
|