@colyseus/schema 3.0.42 → 3.0.44
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 +365 -219
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +365 -219
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +365 -219
- package/lib/Schema.d.ts +4 -3
- package/lib/Schema.js +22 -4
- package/lib/Schema.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +130 -0
- package/lib/bench_encode.js.map +1 -0
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +51 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/Decoder.js +8 -9
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +57 -7
- package/lib/encoder/ChangeTree.js +172 -106
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/Encoder.js +19 -20
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +9 -8
- package/lib/encoder/Root.js +84 -27
- package/lib/encoder/Root.js.map +1 -1
- package/lib/encoder/StateView.d.ts +1 -1
- package/lib/encoder/StateView.js +28 -23
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/types/custom/ArraySchema.js +7 -5
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/custom/CollectionSchema.js +1 -1
- package/lib/types/custom/CollectionSchema.js.map +1 -1
- package/lib/types/custom/MapSchema.js +9 -4
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/symbols.d.ts +14 -14
- package/lib/types/symbols.js +14 -14
- package/lib/types/symbols.js.map +1 -1
- package/lib/utils.js +7 -3
- package/lib/utils.js.map +1 -1
- package/package.json +2 -1
- package/src/Schema.ts +23 -7
- package/src/bench_encode.ts +108 -0
- package/src/debug.ts +55 -0
- package/src/decoder/Decoder.ts +9 -13
- package/src/encoder/ChangeTree.ts +203 -116
- package/src/encoder/Encoder.ts +21 -19
- package/src/encoder/Root.ts +90 -29
- package/src/encoder/StateView.ts +34 -26
- package/src/types/custom/ArraySchema.ts +8 -6
- package/src/types/custom/CollectionSchema.ts +1 -1
- package/src/types/custom/MapSchema.ts +10 -4
- package/src/types/symbols.ts +15 -15
- package/src/utils.ts +9 -3
package/build/umd/index.js
CHANGED
|
@@ -32,37 +32,37 @@
|
|
|
32
32
|
|
|
33
33
|
Symbol.metadata ??= Symbol.for("Symbol.metadata");
|
|
34
34
|
|
|
35
|
-
const $track =
|
|
36
|
-
const $encoder =
|
|
37
|
-
const $decoder =
|
|
38
|
-
const $filter =
|
|
39
|
-
const $getByIndex =
|
|
40
|
-
const $deleteByIndex =
|
|
35
|
+
const $track = "~track";
|
|
36
|
+
const $encoder = "~encoder";
|
|
37
|
+
const $decoder = "~decoder";
|
|
38
|
+
const $filter = "~filter";
|
|
39
|
+
const $getByIndex = "~getByIndex";
|
|
40
|
+
const $deleteByIndex = "~deleteByIndex";
|
|
41
41
|
/**
|
|
42
42
|
* Used to hold ChangeTree instances whitin the structures
|
|
43
43
|
*/
|
|
44
|
-
const $changes =
|
|
44
|
+
const $changes = '~changes';
|
|
45
45
|
/**
|
|
46
46
|
* Used to keep track of the type of the child elements of a collection
|
|
47
47
|
* (MapSchema, ArraySchema, etc.)
|
|
48
48
|
*/
|
|
49
|
-
const $childType =
|
|
49
|
+
const $childType = '~childType';
|
|
50
50
|
/**
|
|
51
51
|
* Optional "discard" method for custom types (ArraySchema)
|
|
52
52
|
* (Discards changes for next serialization)
|
|
53
53
|
*/
|
|
54
|
-
const $onEncodeEnd =
|
|
54
|
+
const $onEncodeEnd = '~onEncodeEnd';
|
|
55
55
|
/**
|
|
56
56
|
* When decoding, this method is called after the instance is fully decoded
|
|
57
57
|
*/
|
|
58
|
-
const $onDecodeEnd =
|
|
58
|
+
const $onDecodeEnd = "~onDecodeEnd";
|
|
59
59
|
/**
|
|
60
60
|
* Metadata
|
|
61
61
|
*/
|
|
62
|
-
const $descriptors =
|
|
63
|
-
const $numFields = "
|
|
64
|
-
const $refTypeFieldIndexes = "
|
|
65
|
-
const $viewFieldIndexes = "
|
|
62
|
+
const $descriptors = "~descriptors";
|
|
63
|
+
const $numFields = "~__numFields";
|
|
64
|
+
const $refTypeFieldIndexes = "~__refTypeFieldIndexes";
|
|
65
|
+
const $viewFieldIndexes = "~__viewFieldIndexes";
|
|
66
66
|
const $fieldIndexesByViewTag = "$__fieldIndexesByViewTag";
|
|
67
67
|
|
|
68
68
|
/**
|
|
@@ -970,6 +970,24 @@
|
|
|
970
970
|
function createChangeSet() {
|
|
971
971
|
return { indexes: {}, operations: [] };
|
|
972
972
|
}
|
|
973
|
+
// Linked list helper functions
|
|
974
|
+
function createChangeTreeList() {
|
|
975
|
+
return { next: undefined, tail: undefined, length: 0 };
|
|
976
|
+
}
|
|
977
|
+
function addToChangeTreeList(list, changeTree) {
|
|
978
|
+
const node = { changeTree, next: undefined, prev: undefined };
|
|
979
|
+
if (!list.next) {
|
|
980
|
+
list.next = node;
|
|
981
|
+
list.tail = node;
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
node.prev = list.tail;
|
|
985
|
+
list.tail.next = node;
|
|
986
|
+
list.tail = node;
|
|
987
|
+
}
|
|
988
|
+
list.length++;
|
|
989
|
+
return node;
|
|
990
|
+
}
|
|
973
991
|
function setOperationAtIndex(changeSet, index) {
|
|
974
992
|
const operationsIndex = changeSet.indexes[index];
|
|
975
993
|
if (operationsIndex === undefined) {
|
|
@@ -994,13 +1012,15 @@
|
|
|
994
1012
|
changeSet.operations[operationsIndex] = undefined;
|
|
995
1013
|
delete changeSet.indexes[index];
|
|
996
1014
|
}
|
|
997
|
-
function enqueueChangeTree(root, changeTree, changeSet,
|
|
1015
|
+
function enqueueChangeTree(root, changeTree, changeSet, queueRootNode = changeTree[changeSet].queueRootNode) {
|
|
1016
|
+
// skip
|
|
998
1017
|
if (!root) {
|
|
999
|
-
// skip
|
|
1000
1018
|
return;
|
|
1001
1019
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1020
|
+
if (queueRootNode) ;
|
|
1021
|
+
else {
|
|
1022
|
+
// Add to linked list if not already present
|
|
1023
|
+
changeTree[changeSet].queueRootNode = addToChangeTreeList(root[changeSet], changeTree);
|
|
1004
1024
|
}
|
|
1005
1025
|
}
|
|
1006
1026
|
class ChangeTree {
|
|
@@ -1024,83 +1044,59 @@
|
|
|
1024
1044
|
*/
|
|
1025
1045
|
this.isNew = true;
|
|
1026
1046
|
this.ref = ref;
|
|
1047
|
+
this.metadata = ref.constructor[Symbol.metadata];
|
|
1027
1048
|
//
|
|
1028
1049
|
// Does this structure have "filters" declared?
|
|
1029
1050
|
//
|
|
1030
|
-
|
|
1031
|
-
if (metadata?.[$viewFieldIndexes]) {
|
|
1051
|
+
if (this.metadata?.[$viewFieldIndexes]) {
|
|
1032
1052
|
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
1033
1053
|
this.filteredChanges = { indexes: {}, operations: [] };
|
|
1034
1054
|
}
|
|
1035
1055
|
}
|
|
1036
1056
|
setRoot(root) {
|
|
1037
1057
|
this.root = root;
|
|
1038
|
-
this.
|
|
1039
|
-
|
|
1040
|
-
// TODO: refactor and possibly unify .setRoot() and .setParent()
|
|
1041
|
-
//
|
|
1058
|
+
const isNewChangeTree = this.root.add(this);
|
|
1059
|
+
this.checkIsFiltered(this.parent, this.parentIndex, isNewChangeTree);
|
|
1042
1060
|
// Recursively set root on child structures
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
const changeTree = this.ref[field.name]?.[$changes];
|
|
1048
|
-
if (changeTree) {
|
|
1049
|
-
if (changeTree.root !== root) {
|
|
1050
|
-
changeTree.setRoot(root);
|
|
1051
|
-
}
|
|
1052
|
-
else {
|
|
1053
|
-
root.add(changeTree); // increment refCount
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
else if (this.ref[$childType] && typeof (this.ref[$childType]) !== "string") {
|
|
1059
|
-
// MapSchema / ArraySchema, etc.
|
|
1060
|
-
this.ref.forEach((value, key) => {
|
|
1061
|
-
const changeTree = value[$changes];
|
|
1062
|
-
if (changeTree.root !== root) {
|
|
1063
|
-
changeTree.setRoot(root);
|
|
1061
|
+
if (isNewChangeTree) {
|
|
1062
|
+
this.forEachChild((child, _) => {
|
|
1063
|
+
if (child.root !== root) {
|
|
1064
|
+
child.setRoot(root);
|
|
1064
1065
|
}
|
|
1065
1066
|
else {
|
|
1066
|
-
root.add(
|
|
1067
|
+
root.add(child); // increment refCount
|
|
1067
1068
|
}
|
|
1068
1069
|
});
|
|
1069
1070
|
}
|
|
1070
1071
|
}
|
|
1071
1072
|
setParent(parent, root, parentIndex) {
|
|
1072
|
-
this.parent
|
|
1073
|
-
this.parentIndex = parentIndex;
|
|
1073
|
+
this.addParent(parent, parentIndex);
|
|
1074
1074
|
// avoid setting parents with empty `root`
|
|
1075
1075
|
if (!root) {
|
|
1076
1076
|
return;
|
|
1077
1077
|
}
|
|
1078
|
+
const isNewChangeTree = root.add(this);
|
|
1078
1079
|
// skip if parent is already set
|
|
1079
1080
|
if (root !== this.root) {
|
|
1080
1081
|
this.root = root;
|
|
1081
|
-
this.checkIsFiltered(parent, parentIndex);
|
|
1082
|
-
}
|
|
1083
|
-
else {
|
|
1084
|
-
root.add(this);
|
|
1082
|
+
this.checkIsFiltered(parent, parentIndex, isNewChangeTree);
|
|
1085
1083
|
}
|
|
1086
1084
|
// assign same parent on child structures
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
if (
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
const changeTree = value[$changes];
|
|
1101
|
-
if (changeTree.root !== root) {
|
|
1102
|
-
changeTree.setParent(this.ref, root, this.indexes[key] ?? key);
|
|
1085
|
+
if (isNewChangeTree) {
|
|
1086
|
+
//
|
|
1087
|
+
// assign same parent on child structures
|
|
1088
|
+
//
|
|
1089
|
+
this.forEachChild((child, index) => {
|
|
1090
|
+
if (child.root === root) {
|
|
1091
|
+
//
|
|
1092
|
+
// re-assigning a child of the same root, move it to the end
|
|
1093
|
+
// of the changes queue so encoding order is preserved
|
|
1094
|
+
//
|
|
1095
|
+
root.add(child);
|
|
1096
|
+
root.moveToEndOfChanges(child);
|
|
1097
|
+
return;
|
|
1103
1098
|
}
|
|
1099
|
+
child.setParent(this.ref, root, index);
|
|
1104
1100
|
});
|
|
1105
1101
|
}
|
|
1106
1102
|
}
|
|
@@ -1108,21 +1104,23 @@
|
|
|
1108
1104
|
//
|
|
1109
1105
|
// assign same parent on child structures
|
|
1110
1106
|
//
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1116
|
-
if (value) {
|
|
1117
|
-
callback(value[$changes], index);
|
|
1107
|
+
if (this.ref[$childType]) {
|
|
1108
|
+
if (typeof (this.ref[$childType]) !== "string") {
|
|
1109
|
+
// MapSchema / ArraySchema, etc.
|
|
1110
|
+
for (const [key, value] of this.ref.entries()) {
|
|
1111
|
+
callback(value[$changes], key);
|
|
1118
1112
|
}
|
|
1119
|
-
}
|
|
1113
|
+
}
|
|
1120
1114
|
}
|
|
1121
|
-
else
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1115
|
+
else {
|
|
1116
|
+
for (const index of this.metadata?.[$refTypeFieldIndexes] ?? []) {
|
|
1117
|
+
const field = this.metadata[index];
|
|
1118
|
+
const value = this.ref[field.name];
|
|
1119
|
+
if (!value) {
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
callback(value[$changes], index);
|
|
1123
|
+
}
|
|
1126
1124
|
}
|
|
1127
1125
|
}
|
|
1128
1126
|
operation(op) {
|
|
@@ -1138,8 +1136,7 @@
|
|
|
1138
1136
|
}
|
|
1139
1137
|
}
|
|
1140
1138
|
change(index, operation = exports.OPERATION.ADD) {
|
|
1141
|
-
const
|
|
1142
|
-
const isFiltered = this.isFiltered || (metadata?.[index]?.tag !== undefined);
|
|
1139
|
+
const isFiltered = this.isFiltered || (this.metadata?.[index]?.tag !== undefined);
|
|
1143
1140
|
const changeSet = (isFiltered)
|
|
1144
1141
|
? this.filteredChanges
|
|
1145
1142
|
: this.changes;
|
|
@@ -1229,19 +1226,16 @@
|
|
|
1229
1226
|
}
|
|
1230
1227
|
}
|
|
1231
1228
|
getType(index) {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
//
|
|
1241
|
-
|
|
1242
|
-
//
|
|
1243
|
-
return this.ref[$childType];
|
|
1244
|
-
}
|
|
1229
|
+
return (
|
|
1230
|
+
//
|
|
1231
|
+
// Get the child type from parent structure.
|
|
1232
|
+
// - ["string"] => "string"
|
|
1233
|
+
// - { map: "string" } => "string"
|
|
1234
|
+
// - { set: "string" } => "string"
|
|
1235
|
+
//
|
|
1236
|
+
this.ref[$childType] || // ArraySchema | MapSchema | SetSchema | CollectionSchema
|
|
1237
|
+
this.metadata[index].type // Schema
|
|
1238
|
+
);
|
|
1245
1239
|
}
|
|
1246
1240
|
getChange(index) {
|
|
1247
1241
|
return this.indexedOperations[index];
|
|
@@ -1301,9 +1295,7 @@
|
|
|
1301
1295
|
endEncode(changeSetName) {
|
|
1302
1296
|
this.indexedOperations = {};
|
|
1303
1297
|
// clear changeset
|
|
1304
|
-
this[changeSetName]
|
|
1305
|
-
this[changeSetName].operations.length = 0;
|
|
1306
|
-
this[changeSetName].queueRootIndex = undefined;
|
|
1298
|
+
this[changeSetName] = createChangeSet();
|
|
1307
1299
|
// ArraySchema and MapSchema have a custom "encode end" method
|
|
1308
1300
|
this.ref[$onEncodeEnd]?.();
|
|
1309
1301
|
// Not a new instance anymore
|
|
@@ -1317,20 +1309,14 @@
|
|
|
1317
1309
|
//
|
|
1318
1310
|
this.ref[$onEncodeEnd]?.();
|
|
1319
1311
|
this.indexedOperations = {};
|
|
1320
|
-
this.changes
|
|
1321
|
-
this.changes.operations.length = 0;
|
|
1322
|
-
this.changes.queueRootIndex = undefined;
|
|
1312
|
+
this.changes = createChangeSet();
|
|
1323
1313
|
if (this.filteredChanges !== undefined) {
|
|
1324
|
-
this.filteredChanges
|
|
1325
|
-
this.filteredChanges.operations.length = 0;
|
|
1326
|
-
this.filteredChanges.queueRootIndex = undefined;
|
|
1314
|
+
this.filteredChanges = createChangeSet();
|
|
1327
1315
|
}
|
|
1328
1316
|
if (discardAll) {
|
|
1329
|
-
this.allChanges
|
|
1330
|
-
this.allChanges.operations.length = 0;
|
|
1317
|
+
this.allChanges = createChangeSet();
|
|
1331
1318
|
if (this.allFilteredChanges !== undefined) {
|
|
1332
|
-
this.allFilteredChanges
|
|
1333
|
-
this.allFilteredChanges.operations.length = 0;
|
|
1319
|
+
this.allFilteredChanges = createChangeSet();
|
|
1334
1320
|
}
|
|
1335
1321
|
}
|
|
1336
1322
|
}
|
|
@@ -1348,18 +1334,10 @@
|
|
|
1348
1334
|
}
|
|
1349
1335
|
this.discard();
|
|
1350
1336
|
}
|
|
1351
|
-
ensureRefId() {
|
|
1352
|
-
// skip if refId is already set.
|
|
1353
|
-
if (this.refId !== undefined) {
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
this.refId = this.root.getNextUniqueId();
|
|
1357
|
-
}
|
|
1358
1337
|
get changed() {
|
|
1359
1338
|
return (Object.entries(this.indexedOperations).length > 0);
|
|
1360
1339
|
}
|
|
1361
|
-
checkIsFiltered(parent, parentIndex) {
|
|
1362
|
-
const isNewChangeTree = this.root.add(this);
|
|
1340
|
+
checkIsFiltered(parent, parentIndex, isNewChangeTree) {
|
|
1363
1341
|
if (this.root.types.hasFilters) {
|
|
1364
1342
|
//
|
|
1365
1343
|
// At Schema initialization, the "root" structure might not be available
|
|
@@ -1371,14 +1349,14 @@
|
|
|
1371
1349
|
if (this.filteredChanges !== undefined) {
|
|
1372
1350
|
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
1373
1351
|
if (isNewChangeTree) {
|
|
1374
|
-
this.root
|
|
1352
|
+
enqueueChangeTree(this.root, this, 'allFilteredChanges');
|
|
1375
1353
|
}
|
|
1376
1354
|
}
|
|
1377
1355
|
}
|
|
1378
1356
|
if (!this.isFiltered) {
|
|
1379
1357
|
enqueueChangeTree(this.root, this, 'changes');
|
|
1380
1358
|
if (isNewChangeTree) {
|
|
1381
|
-
this.root
|
|
1359
|
+
enqueueChangeTree(this.root, this, 'allChanges');
|
|
1382
1360
|
}
|
|
1383
1361
|
}
|
|
1384
1362
|
}
|
|
@@ -1435,6 +1413,90 @@
|
|
|
1435
1413
|
}
|
|
1436
1414
|
}
|
|
1437
1415
|
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Get the immediate parent
|
|
1418
|
+
*/
|
|
1419
|
+
get parent() {
|
|
1420
|
+
return this.parentChain?.ref;
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Get the immediate parent index
|
|
1424
|
+
*/
|
|
1425
|
+
get parentIndex() {
|
|
1426
|
+
return this.parentChain?.index;
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Add a parent to the chain
|
|
1430
|
+
*/
|
|
1431
|
+
addParent(parent, index) {
|
|
1432
|
+
// Check if this parent already exists in the chain
|
|
1433
|
+
if (this.hasParent((p, i) => p[$changes] === parent[$changes] && i === index)) {
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
this.parentChain = {
|
|
1437
|
+
ref: parent,
|
|
1438
|
+
index,
|
|
1439
|
+
next: this.parentChain
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Remove a parent from the chain
|
|
1444
|
+
* @param parent - The parent to remove
|
|
1445
|
+
* @returns true if parent was removed
|
|
1446
|
+
*/
|
|
1447
|
+
removeParent(parent = this.parent) {
|
|
1448
|
+
let current = this.parentChain;
|
|
1449
|
+
let previous = null;
|
|
1450
|
+
while (current) {
|
|
1451
|
+
//
|
|
1452
|
+
// FIXME: it is required to check against `$changes` here because
|
|
1453
|
+
// ArraySchema is instance of Proxy
|
|
1454
|
+
//
|
|
1455
|
+
if (current.ref[$changes] === parent[$changes]) {
|
|
1456
|
+
if (previous) {
|
|
1457
|
+
previous.next = current.next;
|
|
1458
|
+
}
|
|
1459
|
+
else {
|
|
1460
|
+
this.parentChain = current.next;
|
|
1461
|
+
}
|
|
1462
|
+
return true;
|
|
1463
|
+
}
|
|
1464
|
+
previous = current;
|
|
1465
|
+
current = current.next;
|
|
1466
|
+
}
|
|
1467
|
+
return this.parentChain === undefined;
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Find a specific parent in the chain
|
|
1471
|
+
*/
|
|
1472
|
+
findParent(predicate) {
|
|
1473
|
+
let current = this.parentChain;
|
|
1474
|
+
while (current) {
|
|
1475
|
+
if (predicate(current.ref, current.index)) {
|
|
1476
|
+
return current;
|
|
1477
|
+
}
|
|
1478
|
+
current = current.next;
|
|
1479
|
+
}
|
|
1480
|
+
return undefined;
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Check if this ChangeTree has a specific parent
|
|
1484
|
+
*/
|
|
1485
|
+
hasParent(predicate) {
|
|
1486
|
+
return this.findParent(predicate) !== undefined;
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Get all parents as an array (for debugging/testing)
|
|
1490
|
+
*/
|
|
1491
|
+
getAllParents() {
|
|
1492
|
+
const parents = [];
|
|
1493
|
+
let current = this.parentChain;
|
|
1494
|
+
while (current) {
|
|
1495
|
+
parents.push({ ref: current.ref, index: current.index });
|
|
1496
|
+
current = current.next;
|
|
1497
|
+
}
|
|
1498
|
+
return parents;
|
|
1499
|
+
}
|
|
1438
1500
|
}
|
|
1439
1501
|
|
|
1440
1502
|
function encodeValue(encoder, bytes, type, value, operation, it) {
|
|
@@ -1952,7 +2014,7 @@
|
|
|
1952
2014
|
}
|
|
1953
2015
|
if (previousValue !== undefined) {
|
|
1954
2016
|
// remove root reference from previous value
|
|
1955
|
-
previousValue[$changes].root?.remove(previousValue[$changes]);
|
|
2017
|
+
previousValue[$changes].root?.remove(previousValue[$changes], obj);
|
|
1956
2018
|
}
|
|
1957
2019
|
}
|
|
1958
2020
|
else {
|
|
@@ -1983,8 +2045,11 @@
|
|
|
1983
2045
|
return Reflect.has(obj, key);
|
|
1984
2046
|
}
|
|
1985
2047
|
});
|
|
1986
|
-
this
|
|
1987
|
-
|
|
2048
|
+
Object.defineProperty(this, $changes, {
|
|
2049
|
+
value: new ChangeTree(proxy),
|
|
2050
|
+
enumerable: false,
|
|
2051
|
+
writable: true,
|
|
2052
|
+
});
|
|
1988
2053
|
if (items.length > 0) {
|
|
1989
2054
|
this.push(...items);
|
|
1990
2055
|
}
|
|
@@ -2106,7 +2171,7 @@
|
|
|
2106
2171
|
const changeTree = this[$changes];
|
|
2107
2172
|
// remove children references
|
|
2108
2173
|
changeTree.forEachChild((childChangeTree, _) => {
|
|
2109
|
-
changeTree.root?.remove(childChangeTree);
|
|
2174
|
+
changeTree.root?.remove(childChangeTree, this);
|
|
2110
2175
|
});
|
|
2111
2176
|
changeTree.discard(true);
|
|
2112
2177
|
changeTree.operation(exports.OPERATION.CLEAR);
|
|
@@ -2145,7 +2210,6 @@
|
|
|
2145
2210
|
if (this.items.length === 0) {
|
|
2146
2211
|
return undefined;
|
|
2147
2212
|
}
|
|
2148
|
-
// const index = Number(Object.keys(changeTree.indexes)[0]);
|
|
2149
2213
|
const changeTree = this[$changes];
|
|
2150
2214
|
const index = this.tmpItems.findIndex(item => item === this.items[0]);
|
|
2151
2215
|
const allChangesIndex = this.items.findIndex(item => item === this.items[0]);
|
|
@@ -2604,8 +2668,13 @@
|
|
|
2604
2668
|
this.$items = new Map();
|
|
2605
2669
|
this.$indexes = new Map();
|
|
2606
2670
|
this.deletedItems = {};
|
|
2607
|
-
|
|
2608
|
-
|
|
2671
|
+
const changeTree = new ChangeTree(this);
|
|
2672
|
+
changeTree.indexes = {};
|
|
2673
|
+
Object.defineProperty(this, $changes, {
|
|
2674
|
+
value: changeTree,
|
|
2675
|
+
enumerable: false,
|
|
2676
|
+
writable: true,
|
|
2677
|
+
});
|
|
2609
2678
|
if (initialValues) {
|
|
2610
2679
|
if (initialValues instanceof Map ||
|
|
2611
2680
|
initialValues instanceof MapSchema) {
|
|
@@ -2656,7 +2725,7 @@
|
|
|
2656
2725
|
operation = exports.OPERATION.DELETE_AND_ADD;
|
|
2657
2726
|
// remove reference from previous value
|
|
2658
2727
|
if (previousValue !== undefined) {
|
|
2659
|
-
previousValue[$changes].root?.remove(previousValue[$changes]);
|
|
2728
|
+
previousValue[$changes].root?.remove(previousValue[$changes], this);
|
|
2660
2729
|
}
|
|
2661
2730
|
}
|
|
2662
2731
|
}
|
|
@@ -2693,7 +2762,7 @@
|
|
|
2693
2762
|
changeTree.indexes = {};
|
|
2694
2763
|
// remove children references
|
|
2695
2764
|
changeTree.forEachChild((childChangeTree, _) => {
|
|
2696
|
-
changeTree.root?.remove(childChangeTree);
|
|
2765
|
+
changeTree.root?.remove(childChangeTree, this);
|
|
2697
2766
|
});
|
|
2698
2767
|
// clear previous indexes
|
|
2699
2768
|
this.$indexes.clear();
|
|
@@ -3149,10 +3218,13 @@
|
|
|
3149
3218
|
refs: []
|
|
3150
3219
|
};
|
|
3151
3220
|
// for (const refId in $root.changes) {
|
|
3152
|
-
$root.changes.
|
|
3221
|
+
let current = $root.changes.next;
|
|
3222
|
+
while (current) {
|
|
3223
|
+
const changeTree = current.changeTree;
|
|
3153
3224
|
// skip if ChangeTree is undefined
|
|
3154
3225
|
if (changeTree === undefined) {
|
|
3155
|
-
|
|
3226
|
+
current = current.next;
|
|
3227
|
+
continue;
|
|
3156
3228
|
}
|
|
3157
3229
|
const changes = changeTree.indexedOperations;
|
|
3158
3230
|
dump.refs.push(`refId#${changeTree.refId}`);
|
|
@@ -3164,7 +3236,8 @@
|
|
|
3164
3236
|
}
|
|
3165
3237
|
dump.ops[exports.OPERATION[op]]++;
|
|
3166
3238
|
}
|
|
3167
|
-
|
|
3239
|
+
current = current.next;
|
|
3240
|
+
}
|
|
3168
3241
|
return dump;
|
|
3169
3242
|
}
|
|
3170
3243
|
|
|
@@ -3314,7 +3387,7 @@
|
|
|
3314
3387
|
* @param showContents display JSON contents of the instance
|
|
3315
3388
|
* @returns
|
|
3316
3389
|
*/
|
|
3317
|
-
static debugRefIds(ref, showContents = false, level = 0, decoder) {
|
|
3390
|
+
static debugRefIds(ref, showContents = false, level = 0, decoder, keyPrefix = "") {
|
|
3318
3391
|
const contents = (showContents) ? ` - ${JSON.stringify(ref.toJSON())}` : "";
|
|
3319
3392
|
const changeTree = ref[$changes];
|
|
3320
3393
|
const refId = (decoder) ? decoder.root.refIds.get(ref) : changeTree.refId;
|
|
@@ -3323,11 +3396,25 @@
|
|
|
3323
3396
|
const refCount = (root?.refCount?.[refId] > 1)
|
|
3324
3397
|
? ` [×${root.refCount[refId]}]`
|
|
3325
3398
|
: '';
|
|
3326
|
-
let output = `${getIndent(level)}${ref.constructor.name} (refId: ${refId})${refCount}${contents}\n`;
|
|
3327
|
-
changeTree.forEachChild((childChangeTree) =>
|
|
3399
|
+
let output = `${getIndent(level)}${keyPrefix}${ref.constructor.name} (refId: ${refId})${refCount}${contents}\n`;
|
|
3400
|
+
changeTree.forEachChild((childChangeTree, key) => {
|
|
3401
|
+
const keyPrefix = (ref['forEach'] !== undefined && key !== undefined) ? `["${key}"]: ` : "";
|
|
3402
|
+
output += this.debugRefIds(childChangeTree.ref, showContents, level + 1, decoder, keyPrefix);
|
|
3403
|
+
});
|
|
3328
3404
|
return output;
|
|
3329
3405
|
}
|
|
3330
|
-
static
|
|
3406
|
+
static debugRefIdEncodingOrder(ref, changeSet = 'allChanges') {
|
|
3407
|
+
let encodeOrder = [];
|
|
3408
|
+
let current = ref[$changes].root[changeSet].next;
|
|
3409
|
+
while (current) {
|
|
3410
|
+
if (current.changeTree) {
|
|
3411
|
+
encodeOrder.push(current.changeTree.refId);
|
|
3412
|
+
}
|
|
3413
|
+
current = current.next;
|
|
3414
|
+
}
|
|
3415
|
+
return encodeOrder;
|
|
3416
|
+
}
|
|
3417
|
+
static debugRefIdsFromDecoder(decoder) {
|
|
3331
3418
|
return this.debugRefIds(decoder.state, false, 0, decoder);
|
|
3332
3419
|
}
|
|
3333
3420
|
/**
|
|
@@ -3376,8 +3463,12 @@
|
|
|
3376
3463
|
const changeTrees = new Map();
|
|
3377
3464
|
const instanceRefIds = [];
|
|
3378
3465
|
let totalOperations = 0;
|
|
3466
|
+
// TODO: FIXME: this method is not working as expected
|
|
3379
3467
|
for (const [refId, changes] of Object.entries(root[changeSetName])) {
|
|
3380
3468
|
const changeTree = root.changeTrees[refId];
|
|
3469
|
+
if (!changeTree) {
|
|
3470
|
+
continue;
|
|
3471
|
+
}
|
|
3381
3472
|
let includeChangeTree = false;
|
|
3382
3473
|
let parentChangeTrees = [];
|
|
3383
3474
|
let parentChangeTree = changeTree.parent?.[$changes];
|
|
@@ -3513,7 +3604,7 @@
|
|
|
3513
3604
|
changeTree.indexes = {};
|
|
3514
3605
|
// remove children references
|
|
3515
3606
|
changeTree.forEachChild((childChangeTree, _) => {
|
|
3516
|
-
changeTree.root?.remove(childChangeTree);
|
|
3607
|
+
changeTree.root?.remove(childChangeTree, this);
|
|
3517
3608
|
});
|
|
3518
3609
|
// clear previous indexes
|
|
3519
3610
|
this.$indexes.clear();
|
|
@@ -3800,18 +3891,20 @@
|
|
|
3800
3891
|
this.refCount = {};
|
|
3801
3892
|
this.changeTrees = {};
|
|
3802
3893
|
// all changes
|
|
3803
|
-
this.allChanges =
|
|
3804
|
-
this.allFilteredChanges =
|
|
3894
|
+
this.allChanges = createChangeTreeList();
|
|
3895
|
+
this.allFilteredChanges = createChangeTreeList(); // TODO: do not initialize it if filters are not used
|
|
3805
3896
|
// pending changes to be encoded
|
|
3806
|
-
this.changes =
|
|
3807
|
-
this.filteredChanges =
|
|
3897
|
+
this.changes = createChangeTreeList();
|
|
3898
|
+
this.filteredChanges = createChangeTreeList(); // TODO: do not initialize it if filters are not used
|
|
3808
3899
|
}
|
|
3809
3900
|
getNextUniqueId() {
|
|
3810
3901
|
return this.nextUniqueId++;
|
|
3811
3902
|
}
|
|
3812
3903
|
add(changeTree) {
|
|
3813
|
-
//
|
|
3814
|
-
changeTree.
|
|
3904
|
+
// Assign unique `refId` to changeTree if it doesn't have one yet.
|
|
3905
|
+
if (changeTree.refId === undefined) {
|
|
3906
|
+
changeTree.refId = this.getNextUniqueId();
|
|
3907
|
+
}
|
|
3815
3908
|
const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);
|
|
3816
3909
|
if (isNewChangeTree) {
|
|
3817
3910
|
this.changeTrees[changeTree.refId] = changeTree;
|
|
@@ -3830,10 +3923,12 @@
|
|
|
3830
3923
|
}
|
|
3831
3924
|
}
|
|
3832
3925
|
this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;
|
|
3926
|
+
// console.log("ADD", { refId: changeTree.refId, refCount: this.refCount[changeTree.refId] });
|
|
3833
3927
|
return isNewChangeTree;
|
|
3834
3928
|
}
|
|
3835
|
-
remove(changeTree) {
|
|
3929
|
+
remove(changeTree, parent) {
|
|
3836
3930
|
const refCount = (this.refCount[changeTree.refId]) - 1;
|
|
3931
|
+
// console.log("REMOVE", { refId: changeTree.refId, refCount });
|
|
3837
3932
|
if (refCount <= 0) {
|
|
3838
3933
|
//
|
|
3839
3934
|
// Only remove "root" reference if it's the last reference
|
|
@@ -3847,7 +3942,19 @@
|
|
|
3847
3942
|
this.removeChangeFromChangeSet("filteredChanges", changeTree);
|
|
3848
3943
|
}
|
|
3849
3944
|
this.refCount[changeTree.refId] = 0;
|
|
3850
|
-
changeTree.forEachChild((child, _) =>
|
|
3945
|
+
changeTree.forEachChild((child, _) => {
|
|
3946
|
+
if (child.removeParent(changeTree.ref)) {
|
|
3947
|
+
if ((child.parentChain === undefined || // no parent, remove it
|
|
3948
|
+
(child.parentChain && this.refCount[child.refId] > 1) // parent is still in use, but has more than one reference, remove it
|
|
3949
|
+
)) {
|
|
3950
|
+
this.remove(child, changeTree.ref);
|
|
3951
|
+
}
|
|
3952
|
+
else if (child.parentChain) {
|
|
3953
|
+
// re-assigning a child of the same root, move it to the end
|
|
3954
|
+
this.moveToEndOfChanges(child);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
});
|
|
3851
3958
|
}
|
|
3852
3959
|
else {
|
|
3853
3960
|
this.refCount[changeTree.refId] = refCount;
|
|
@@ -3860,32 +3967,73 @@
|
|
|
3860
3967
|
// containing instance is not available, the Decoder will throw
|
|
3861
3968
|
// "refId not found" error.
|
|
3862
3969
|
//
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
enqueueChangeTree(this, changeTree, "filteredChanges");
|
|
3866
|
-
}
|
|
3867
|
-
else {
|
|
3868
|
-
this.removeChangeFromChangeSet("changes", changeTree);
|
|
3869
|
-
enqueueChangeTree(this, changeTree, "changes");
|
|
3870
|
-
}
|
|
3970
|
+
this.moveToEndOfChanges(changeTree);
|
|
3971
|
+
changeTree.forEachChild((child, _) => this.moveToEndOfChanges(child));
|
|
3871
3972
|
}
|
|
3872
3973
|
return refCount;
|
|
3873
3974
|
}
|
|
3975
|
+
moveToEndOfChanges(changeTree) {
|
|
3976
|
+
if (changeTree.filteredChanges) {
|
|
3977
|
+
this.moveToEndOfChangeTreeList("filteredChanges", changeTree);
|
|
3978
|
+
this.moveToEndOfChangeTreeList("allFilteredChanges", changeTree);
|
|
3979
|
+
}
|
|
3980
|
+
else {
|
|
3981
|
+
this.moveToEndOfChangeTreeList("changes", changeTree);
|
|
3982
|
+
this.moveToEndOfChangeTreeList("allChanges", changeTree);
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
moveToEndOfChangeTreeList(changeSetName, changeTree) {
|
|
3986
|
+
const changeSet = this[changeSetName];
|
|
3987
|
+
const node = changeTree[changeSetName].queueRootNode;
|
|
3988
|
+
if (!node || node === changeSet.tail)
|
|
3989
|
+
return;
|
|
3990
|
+
// Remove from current position
|
|
3991
|
+
if (node.prev) {
|
|
3992
|
+
node.prev.next = node.next;
|
|
3993
|
+
}
|
|
3994
|
+
else {
|
|
3995
|
+
changeSet.next = node.next;
|
|
3996
|
+
}
|
|
3997
|
+
if (node.next) {
|
|
3998
|
+
node.next.prev = node.prev;
|
|
3999
|
+
}
|
|
4000
|
+
else {
|
|
4001
|
+
changeSet.tail = node.prev;
|
|
4002
|
+
}
|
|
4003
|
+
// Add to end
|
|
4004
|
+
node.prev = changeSet.tail;
|
|
4005
|
+
node.next = undefined;
|
|
4006
|
+
if (changeSet.tail) {
|
|
4007
|
+
changeSet.tail.next = node;
|
|
4008
|
+
}
|
|
4009
|
+
else {
|
|
4010
|
+
changeSet.next = node;
|
|
4011
|
+
}
|
|
4012
|
+
changeSet.tail = node;
|
|
4013
|
+
}
|
|
3874
4014
|
removeChangeFromChangeSet(changeSetName, changeTree) {
|
|
3875
4015
|
const changeSet = this[changeSetName];
|
|
3876
|
-
const
|
|
3877
|
-
if (
|
|
3878
|
-
|
|
3879
|
-
|
|
4016
|
+
const node = changeTree[changeSetName].queueRootNode;
|
|
4017
|
+
if (node && node.changeTree === changeTree) {
|
|
4018
|
+
// Remove the node from the linked list
|
|
4019
|
+
if (node.prev) {
|
|
4020
|
+
node.prev.next = node.next;
|
|
4021
|
+
}
|
|
4022
|
+
else {
|
|
4023
|
+
changeSet.next = node.next;
|
|
4024
|
+
}
|
|
4025
|
+
if (node.next) {
|
|
4026
|
+
node.next.prev = node.prev;
|
|
4027
|
+
}
|
|
4028
|
+
else {
|
|
4029
|
+
changeSet.tail = node.prev;
|
|
4030
|
+
}
|
|
4031
|
+
changeSet.length--;
|
|
4032
|
+
// Clear ChangeTree reference
|
|
4033
|
+
changeTree[changeSetName].queueRootNode = undefined;
|
|
3880
4034
|
return true;
|
|
3881
4035
|
}
|
|
3882
|
-
|
|
3883
|
-
// changeTree[changeSetName].queueRootIndex = -1;
|
|
3884
|
-
// return true;
|
|
3885
|
-
// }
|
|
3886
|
-
}
|
|
3887
|
-
clear() {
|
|
3888
|
-
this.changes.length = 0;
|
|
4036
|
+
return false;
|
|
3889
4037
|
}
|
|
3890
4038
|
}
|
|
3891
4039
|
|
|
@@ -3915,12 +4063,9 @@
|
|
|
3915
4063
|
) {
|
|
3916
4064
|
const hasView = (view !== undefined);
|
|
3917
4065
|
const rootChangeTree = this.state[$changes];
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
const changeTree =
|
|
3921
|
-
if (!changeTree) {
|
|
3922
|
-
continue;
|
|
3923
|
-
}
|
|
4066
|
+
let current = this.root[changeSetName];
|
|
4067
|
+
while (current = current.next) {
|
|
4068
|
+
const changeTree = current.changeTree;
|
|
3924
4069
|
if (hasView) {
|
|
3925
4070
|
if (!view.isChangeTreeVisible(changeTree)) {
|
|
3926
4071
|
// console.log("MARK AS INVISIBLE:", { ref: changeTree.ref.constructor.name, refId: changeTree.refId, raw: changeTree.ref.toJSON() });
|
|
@@ -4009,7 +4154,9 @@
|
|
|
4009
4154
|
const rootChangeSet = (typeof (field) === "string")
|
|
4010
4155
|
? this.root[field]
|
|
4011
4156
|
: field;
|
|
4012
|
-
rootChangeSet.
|
|
4157
|
+
let current = rootChangeSet.next;
|
|
4158
|
+
while (current) {
|
|
4159
|
+
const changeTree = current.changeTree;
|
|
4013
4160
|
const changeSet = changeTree[field];
|
|
4014
4161
|
const metadata = changeTree.ref.constructor[Symbol.metadata];
|
|
4015
4162
|
console.log("->", { ref: changeTree.ref.constructor.name, refId: changeTree.refId, changes: Object.keys(changeSet).length });
|
|
@@ -4021,7 +4168,8 @@
|
|
|
4021
4168
|
op: exports.OPERATION[op],
|
|
4022
4169
|
});
|
|
4023
4170
|
}
|
|
4024
|
-
|
|
4171
|
+
current = current.next;
|
|
4172
|
+
}
|
|
4025
4173
|
}
|
|
4026
4174
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
4027
4175
|
const viewOffset = it.offset;
|
|
@@ -4071,21 +4219,19 @@
|
|
|
4071
4219
|
}
|
|
4072
4220
|
discardChanges() {
|
|
4073
4221
|
// discard shared changes
|
|
4074
|
-
let
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
}
|
|
4079
|
-
this.root.changes.length = 0;
|
|
4222
|
+
let current = this.root.changes.next;
|
|
4223
|
+
while (current) {
|
|
4224
|
+
current.changeTree.endEncode('changes');
|
|
4225
|
+
current = current.next;
|
|
4080
4226
|
}
|
|
4227
|
+
this.root.changes = createChangeTreeList();
|
|
4081
4228
|
// discard filtered changes
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
}
|
|
4087
|
-
this.root.filteredChanges.length = 0;
|
|
4229
|
+
current = this.root.filteredChanges.next;
|
|
4230
|
+
while (current) {
|
|
4231
|
+
current.changeTree.endEncode('filteredChanges');
|
|
4232
|
+
current = current.next;
|
|
4088
4233
|
}
|
|
4234
|
+
this.root.filteredChanges = createChangeTreeList();
|
|
4089
4235
|
}
|
|
4090
4236
|
tryEncodeTypeId(bytes, baseType, targetType, it) {
|
|
4091
4237
|
const baseTypeId = this.context.getTypeId(baseType);
|
|
@@ -4275,21 +4421,20 @@
|
|
|
4275
4421
|
//
|
|
4276
4422
|
if (bytes[it.offset] == SWITCH_TO_STRUCTURE) {
|
|
4277
4423
|
it.offset++;
|
|
4424
|
+
ref[$onDecodeEnd]?.();
|
|
4278
4425
|
const nextRefId = decode.number(bytes, it);
|
|
4279
4426
|
const nextRef = $root.refs.get(nextRefId);
|
|
4280
4427
|
//
|
|
4281
4428
|
// Trying to access a reference that haven't been decoded yet.
|
|
4282
4429
|
//
|
|
4283
4430
|
if (!nextRef) {
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4431
|
+
throw new Error(`"refId" not found: ${nextRefId}`);
|
|
4432
|
+
}
|
|
4433
|
+
else {
|
|
4434
|
+
ref = nextRef;
|
|
4435
|
+
decoder = ref.constructor[$decoder];
|
|
4436
|
+
this.currentRefId = nextRefId;
|
|
4288
4437
|
}
|
|
4289
|
-
ref[$onDecodeEnd]?.();
|
|
4290
|
-
this.currentRefId = nextRefId;
|
|
4291
|
-
ref = nextRef;
|
|
4292
|
-
decoder = ref.constructor[$decoder];
|
|
4293
4438
|
continue;
|
|
4294
4439
|
}
|
|
4295
4440
|
const result = decoder(this, bytes, it, ref, allChanges);
|
|
@@ -4333,10 +4478,6 @@
|
|
|
4333
4478
|
return type || defaultType;
|
|
4334
4479
|
}
|
|
4335
4480
|
createInstanceOfType(type) {
|
|
4336
|
-
// let instance: Schema = new (type as any)();
|
|
4337
|
-
// // assign root on $changes
|
|
4338
|
-
// instance[$changes].root = this.root[$changes].root;
|
|
4339
|
-
// return instance;
|
|
4340
4481
|
return new type();
|
|
4341
4482
|
}
|
|
4342
4483
|
removeChildRefs(ref, allChanges) {
|
|
@@ -4863,11 +5004,12 @@
|
|
|
4863
5004
|
// TODO: allow to set multiple tags at once
|
|
4864
5005
|
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
4865
5006
|
const changeTree = obj?.[$changes];
|
|
5007
|
+
const parentChangeTree = changeTree.parent;
|
|
4866
5008
|
if (!changeTree) {
|
|
4867
5009
|
console.warn("StateView#add(), invalid object:", obj);
|
|
4868
|
-
return
|
|
5010
|
+
return false;
|
|
4869
5011
|
}
|
|
4870
|
-
else if (!
|
|
5012
|
+
else if (!parentChangeTree &&
|
|
4871
5013
|
changeTree.refId !== 0 // allow root object
|
|
4872
5014
|
) {
|
|
4873
5015
|
/**
|
|
@@ -4889,18 +5031,31 @@
|
|
|
4889
5031
|
// add parent ChangeTree's
|
|
4890
5032
|
// - if it was invisible to this view
|
|
4891
5033
|
// - if it were previously filtered out
|
|
4892
|
-
if (checkIncludeParent &&
|
|
5034
|
+
if (checkIncludeParent && parentChangeTree) {
|
|
4893
5035
|
this.addParentOf(changeTree, tag);
|
|
4894
5036
|
}
|
|
4895
|
-
//
|
|
4896
|
-
// TODO: when adding an item of a MapSchema, the changes may not
|
|
4897
|
-
// be set (only the parent's changes are set)
|
|
4898
|
-
//
|
|
4899
5037
|
let changes = this.changes.get(changeTree.refId);
|
|
4900
5038
|
if (changes === undefined) {
|
|
4901
5039
|
changes = {};
|
|
5040
|
+
// FIXME / OPTIMIZE: do not add if no changes are needed
|
|
4902
5041
|
this.changes.set(changeTree.refId, changes);
|
|
4903
5042
|
}
|
|
5043
|
+
let isChildAdded = false;
|
|
5044
|
+
//
|
|
5045
|
+
// Add children of this ChangeTree first.
|
|
5046
|
+
// If successful, we must link the current ChangeTree to the child.
|
|
5047
|
+
//
|
|
5048
|
+
changeTree.forEachChild((change, index) => {
|
|
5049
|
+
// Do not ADD children that don't have the same tag
|
|
5050
|
+
if (metadata &&
|
|
5051
|
+
metadata[index].tag !== undefined &&
|
|
5052
|
+
metadata[index].tag !== tag) {
|
|
5053
|
+
return;
|
|
5054
|
+
}
|
|
5055
|
+
if (this.add(change.ref, tag, false)) {
|
|
5056
|
+
isChildAdded = true;
|
|
5057
|
+
}
|
|
5058
|
+
});
|
|
4904
5059
|
// set tag
|
|
4905
5060
|
if (tag !== DEFAULT_VIEW_TAG) {
|
|
4906
5061
|
if (!this.tags) {
|
|
@@ -4922,11 +5077,12 @@
|
|
|
4922
5077
|
}
|
|
4923
5078
|
});
|
|
4924
5079
|
}
|
|
4925
|
-
else {
|
|
4926
|
-
|
|
5080
|
+
else if (!changeTree.isNew || isChildAdded) {
|
|
5081
|
+
// new structures will be added as part of .encode() call, no need to force it to .encodeView()
|
|
4927
5082
|
const changeSet = (changeTree.filteredChanges !== undefined)
|
|
4928
5083
|
? changeTree.allFilteredChanges
|
|
4929
5084
|
: changeTree.allChanges;
|
|
5085
|
+
const isInvisible = this.invisible.has(changeTree);
|
|
4930
5086
|
for (let i = 0, len = changeSet.operations.length; i < len; i++) {
|
|
4931
5087
|
const index = changeSet.operations[i];
|
|
4932
5088
|
if (index === undefined) {
|
|
@@ -4934,27 +5090,17 @@
|
|
|
4934
5090
|
} // skip "undefined" indexes
|
|
4935
5091
|
const op = changeTree.indexedOperations[index] ?? exports.OPERATION.ADD;
|
|
4936
5092
|
const tagAtIndex = metadata?.[index].tag;
|
|
4937
|
-
if (
|
|
5093
|
+
if (op !== exports.OPERATION.DELETE &&
|
|
4938
5094
|
(isInvisible || // if "invisible", include all
|
|
4939
5095
|
tagAtIndex === undefined || // "all change" with no tag
|
|
4940
5096
|
tagAtIndex === tag // tagged property
|
|
4941
|
-
)
|
|
4942
|
-
op !== exports.OPERATION.DELETE) {
|
|
5097
|
+
)) {
|
|
4943
5098
|
changes[index] = op;
|
|
5099
|
+
isChildAdded = true; // FIXME: assign only once
|
|
4944
5100
|
}
|
|
4945
5101
|
}
|
|
4946
5102
|
}
|
|
4947
|
-
|
|
4948
|
-
changeTree.forEachChild((change, index) => {
|
|
4949
|
-
// Do not ADD children that don't have the same tag
|
|
4950
|
-
if (metadata &&
|
|
4951
|
-
metadata[index].tag !== undefined &&
|
|
4952
|
-
metadata[index].tag !== tag) {
|
|
4953
|
-
return;
|
|
4954
|
-
}
|
|
4955
|
-
this.add(change.ref, tag, false);
|
|
4956
|
-
});
|
|
4957
|
-
return this;
|
|
5103
|
+
return isChildAdded;
|
|
4958
5104
|
}
|
|
4959
5105
|
addParentOf(childChangeTree, tag) {
|
|
4960
5106
|
const changeTree = childChangeTree.parent[$changes];
|