@colyseus/schema 3.0.0-alpha.4 → 3.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -62
- package/bin/schema-debug +94 -0
- package/build/cjs/index.js +2201 -1507
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +2198 -1506
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +2208 -1514
- package/lib/Metadata.d.ts +21 -9
- package/lib/Metadata.js +169 -32
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +19 -4
- package/lib/Reflection.js +66 -32
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +4 -4
- package/lib/Schema.js +44 -50
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +31 -34
- package/lib/annotations.js +110 -160
- package/lib/annotations.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/codegen/api.js +1 -2
- package/lib/codegen/api.js.map +1 -1
- package/lib/codegen/languages/cpp.js +1 -2
- package/lib/codegen/languages/cpp.js.map +1 -1
- package/lib/codegen/languages/csharp.js +2 -46
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +1 -2
- package/lib/codegen/languages/haxe.js.map +1 -1
- package/lib/codegen/languages/java.js +1 -2
- package/lib/codegen/languages/java.js.map +1 -1
- package/lib/codegen/languages/js.js +1 -2
- package/lib/codegen/languages/js.js.map +1 -1
- package/lib/codegen/languages/lua.js +1 -2
- package/lib/codegen/languages/lua.js.map +1 -1
- package/lib/codegen/languages/ts.js +1 -2
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +85 -3
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +6 -3
- package/lib/codegen/types.js.map +1 -1
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +51 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/DecodeOperation.d.ts +3 -4
- package/lib/decoder/DecodeOperation.js +37 -19
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +6 -7
- package/lib/decoder/Decoder.js +14 -14
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/decoder/ReferenceTracker.js +3 -2
- package/lib/decoder/ReferenceTracker.js.map +1 -1
- package/lib/decoder/strategy/RawChanges.js +1 -2
- package/lib/decoder/strategy/RawChanges.js.map +1 -1
- package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
- package/lib/decoder/strategy/StateCallbacks.js +75 -65
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +27 -21
- package/lib/encoder/ChangeTree.js +246 -186
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +3 -6
- package/lib/encoder/EncodeOperation.js +51 -65
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +9 -8
- package/lib/encoder/Encoder.js +168 -91
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +22 -0
- package/lib/encoder/Root.js +81 -0
- package/lib/encoder/Root.js.map +1 -0
- package/lib/encoder/StateView.d.ts +7 -7
- package/lib/encoder/StateView.js +70 -74
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/assert.d.ts +7 -6
- package/lib/encoding/assert.js +13 -5
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.d.ts +35 -20
- package/lib/encoding/decode.js +43 -87
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +36 -17
- package/lib/encoding/encode.js +82 -68
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +4 -5
- package/lib/encoding/spec.js +1 -2
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +10 -9
- package/lib/index.js +24 -17
- package/lib/index.js.map +1 -1
- package/lib/types/HelperTypes.d.ts +34 -2
- package/lib/types/HelperTypes.js.map +1 -1
- package/lib/types/TypeContext.d.ts +23 -0
- package/lib/types/TypeContext.js +111 -0
- package/lib/types/TypeContext.js.map +1 -0
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +33 -22
- 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.d.ts +3 -1
- package/lib/types/custom/MapSchema.js +12 -4
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/custom/SetSchema.js +1 -0
- package/lib/types/custom/SetSchema.js.map +1 -1
- package/lib/types/registry.d.ts +8 -1
- package/lib/types/registry.js +23 -6
- package/lib/types/registry.js.map +1 -1
- package/lib/types/symbols.d.ts +8 -5
- package/lib/types/symbols.js +9 -6
- package/lib/types/symbols.js.map +1 -1
- package/lib/types/utils.js +1 -2
- package/lib/types/utils.js.map +1 -1
- package/lib/utils.js +9 -7
- package/lib/utils.js.map +1 -1
- package/package.json +7 -6
- package/src/Metadata.ts +190 -42
- package/src/Reflection.ts +77 -39
- package/src/Schema.ts +59 -64
- package/src/annotations.ts +156 -202
- package/src/bench_encode.ts +108 -0
- package/src/codegen/languages/csharp.ts +1 -47
- package/src/codegen/parser.ts +107 -0
- package/src/codegen/types.ts +1 -0
- package/src/debug.ts +55 -0
- package/src/decoder/DecodeOperation.ts +46 -18
- package/src/decoder/Decoder.ts +17 -15
- package/src/decoder/ReferenceTracker.ts +3 -2
- package/src/decoder/strategy/StateCallbacks.ts +153 -82
- package/src/encoder/ChangeTree.ts +286 -202
- package/src/encoder/EncodeOperation.ts +78 -78
- package/src/encoder/Encoder.ts +202 -97
- package/src/encoder/Root.ts +93 -0
- package/src/encoder/StateView.ts +76 -88
- package/src/encoding/assert.ts +17 -8
- package/src/encoding/decode.ts +62 -97
- package/src/encoding/encode.ts +99 -65
- package/src/encoding/spec.ts +3 -5
- package/src/index.ts +12 -20
- package/src/types/HelperTypes.ts +54 -2
- package/src/types/TypeContext.ts +133 -0
- package/src/types/custom/ArraySchema.ts +49 -19
- package/src/types/custom/CollectionSchema.ts +1 -0
- package/src/types/custom/MapSchema.ts +18 -5
- package/src/types/custom/SetSchema.ts +1 -0
- package/src/types/registry.ts +22 -3
- package/src/types/symbols.ts +10 -7
- package/src/utils.ts +7 -3
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { OPERATION } from "../encoding/spec";
|
|
2
2
|
import { Schema } from "../Schema";
|
|
3
|
-
import { $changes, $childType, $decoder, $onEncodeEnd, $encoder, $getByIndex, $
|
|
3
|
+
import { $changes, $childType, $decoder, $onEncodeEnd, $encoder, $getByIndex, $refTypeFieldIndexes, $viewFieldIndexes } from "../types/symbols";
|
|
4
4
|
|
|
5
5
|
import type { MapSchema } from "../types/custom/MapSchema";
|
|
6
6
|
import type { ArraySchema } from "../types/custom/ArraySchema";
|
|
7
7
|
import type { CollectionSchema } from "../types/custom/CollectionSchema";
|
|
8
8
|
import type { SetSchema } from "../types/custom/SetSchema";
|
|
9
9
|
|
|
10
|
+
import { Root } from "./Root";
|
|
10
11
|
import { Metadata } from "../Metadata";
|
|
11
12
|
import type { EncodeOperation } from "./EncodeOperation";
|
|
12
13
|
import type { DecodeOperation } from "../decoder/DecodeOperation";
|
|
13
|
-
import type { StateView } from "./StateView";
|
|
14
14
|
|
|
15
15
|
declare global {
|
|
16
16
|
interface Object {
|
|
@@ -27,50 +27,47 @@ export type Ref = Schema
|
|
|
27
27
|
| CollectionSchema
|
|
28
28
|
| SetSchema;
|
|
29
29
|
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
export type ChangeSetName = "changes"
|
|
31
|
+
| "allChanges"
|
|
32
|
+
| "filteredChanges"
|
|
33
|
+
| "allFilteredChanges";
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// pending changes to be encoded
|
|
39
|
-
changes = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
40
|
-
filteredChanges = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
35
|
+
export interface IndexedOperations {
|
|
36
|
+
[index: number]: OPERATION;
|
|
37
|
+
}
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
39
|
+
export interface ChangeSet {
|
|
40
|
+
// field index -> operation index
|
|
41
|
+
indexes: { [index: number]: number };
|
|
42
|
+
operations: OPERATION[]
|
|
43
|
+
queueRootIndex?: number; // index of ChangeTree structure in `root.changes` or `root.filteredChanges`
|
|
44
|
+
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
export function setOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
47
|
+
const operationsIndex = changeSet.indexes[index];
|
|
48
|
+
if (operationsIndex === undefined) {
|
|
49
|
+
changeSet.indexes[index] = changeSet.operations.push(index) - 1;
|
|
50
|
+
} else {
|
|
51
|
+
changeSet.operations[operationsIndex] = index;
|
|
49
52
|
}
|
|
53
|
+
}
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.changes.delete(changeTree);
|
|
56
|
-
|
|
57
|
-
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
58
|
-
this.allFilteredChanges.delete(changeTree);
|
|
59
|
-
this.filteredChanges.delete(changeTree);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
this.refCount.delete(changeTree);
|
|
63
|
-
|
|
64
|
-
} else {
|
|
65
|
-
this.refCount.set(changeTree, refCount - 1);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
changeTree.forEachChild((child, _) =>
|
|
69
|
-
this.remove(child));
|
|
55
|
+
export function deleteOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
56
|
+
const operationsIndex = changeSet.indexes[index];
|
|
57
|
+
if (operationsIndex !== undefined) {
|
|
58
|
+
changeSet.operations[operationsIndex] = undefined;
|
|
70
59
|
}
|
|
60
|
+
delete changeSet.indexes[index];
|
|
61
|
+
}
|
|
71
62
|
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
function enqueueChangeTree(
|
|
64
|
+
root: Root,
|
|
65
|
+
changeTree: ChangeTree,
|
|
66
|
+
changeSet: 'changes' | 'filteredChanges' | 'allFilteredChanges',
|
|
67
|
+
queueRootIndex = changeTree[changeSet].queueRootIndex
|
|
68
|
+
) {
|
|
69
|
+
if (root && root[changeSet][queueRootIndex] !== changeTree) {
|
|
70
|
+
changeTree[changeSet].queueRootIndex = root[changeSet].push(changeTree) - 1;
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
|
|
@@ -79,69 +76,90 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
79
76
|
refId: number;
|
|
80
77
|
|
|
81
78
|
root?: Root;
|
|
82
|
-
|
|
83
|
-
isFiltered?: boolean;
|
|
84
|
-
isPartiallyFiltered?: boolean;
|
|
85
|
-
|
|
86
79
|
parent?: Ref;
|
|
87
80
|
parentIndex?: number;
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
isFiltered: boolean = false;
|
|
83
|
+
isPartiallyFiltered: boolean = false;
|
|
91
84
|
|
|
92
|
-
|
|
93
|
-
allFilteredChanges = new Map<number, OPERATION>();
|
|
85
|
+
indexedOperations: IndexedOperations = {};
|
|
94
86
|
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
//
|
|
88
|
+
// TODO:
|
|
89
|
+
// try storing the index + operation per item.
|
|
90
|
+
// example: 1024 & 1025 => ADD, 1026 => DELETE
|
|
91
|
+
//
|
|
92
|
+
// => https://chatgpt.com/share/67107d0c-bc20-8004-8583-83b17dd7c196
|
|
93
|
+
//
|
|
94
|
+
changes: ChangeSet = { indexes: {}, operations: [] };
|
|
95
|
+
allChanges: ChangeSet = { indexes: {}, operations: [] };
|
|
96
|
+
filteredChanges: ChangeSet;
|
|
97
|
+
allFilteredChanges: ChangeSet;
|
|
97
98
|
|
|
98
|
-
[
|
|
99
|
+
indexes: {[index: string]: any}; // TODO: remove this, only used by MapSchema/SetSchema/CollectionSchema (`encodeKeyValueOperation`)
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Is this a new instance? Used on ArraySchema to determine OPERATION.MOVE_AND_ADD operation.
|
|
103
|
+
*/
|
|
104
|
+
isNew = true;
|
|
99
105
|
|
|
100
106
|
constructor(ref: T) {
|
|
101
107
|
this.ref = ref;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
setRoot(root: Root) {
|
|
105
|
-
this.root = root;
|
|
106
|
-
this.root.add(this);
|
|
107
108
|
|
|
108
109
|
//
|
|
109
|
-
//
|
|
110
|
-
// yet, as it only does once the "Encoder" has been set up.
|
|
111
|
-
//
|
|
112
|
-
// So the "parent" may be already set without a "root".
|
|
110
|
+
// Does this structure have "filters" declared?
|
|
113
111
|
//
|
|
114
|
-
|
|
112
|
+
if (ref.constructor[Symbol.metadata]?.[$viewFieldIndexes]) {
|
|
113
|
+
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
114
|
+
this.filteredChanges = { indexes: {}, operations: [] };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
+
setRoot(root: Root) {
|
|
119
|
+
this.root = root;
|
|
120
|
+
const isNewChangeTree = this.root.add(this);
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
this.root.changes.set(this, this.changes);
|
|
121
|
-
}
|
|
122
|
+
const metadata: Metadata = this.ref.constructor[Symbol.metadata];
|
|
122
123
|
|
|
123
|
-
if (this.
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
if (this.root.types.hasFilters) {
|
|
125
|
+
//
|
|
126
|
+
// At Schema initialization, the "root" structure might not be available
|
|
127
|
+
// yet, as it only does once the "Encoder" has been set up.
|
|
128
|
+
//
|
|
129
|
+
// So the "parent" may be already set without a "root".
|
|
130
|
+
//
|
|
131
|
+
this.checkIsFiltered(metadata, this.parent, this.parentIndex);
|
|
126
132
|
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
134
|
+
enqueueChangeTree(root, this, 'filteredChanges');
|
|
135
|
+
if (isNewChangeTree) {
|
|
136
|
+
this.root.allFilteredChanges.push(this);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
if (!this.isFiltered) {
|
|
132
|
-
|
|
142
|
+
enqueueChangeTree(root, this, 'changes');
|
|
143
|
+
if (isNewChangeTree) {
|
|
144
|
+
this.root.allChanges.push(this);
|
|
145
|
+
}
|
|
133
146
|
}
|
|
134
147
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
148
|
+
// Recursively set root on child structures
|
|
149
|
+
if (metadata) {
|
|
150
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
151
|
+
const field = metadata[index as any as number];
|
|
152
|
+
const value = this.ref[field.name];
|
|
153
|
+
value?.[$changes].setRoot(root);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
} else if (this.ref[$childType] && typeof(this.ref[$childType]) !== "string") {
|
|
157
|
+
// MapSchema / ArraySchema, etc.
|
|
158
|
+
(this.ref as MapSchema).forEach((value, key) => {
|
|
159
|
+
value[$changes].setRoot(root);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
138
162
|
|
|
139
|
-
// this.allChanges.forEach((_, index) => {
|
|
140
|
-
// const childRef = this.ref[$getByIndex](index);
|
|
141
|
-
// if (childRef && childRef[$changes]) {
|
|
142
|
-
// childRef[$changes].setRoot(root);
|
|
143
|
-
// }
|
|
144
|
-
// });
|
|
145
163
|
}
|
|
146
164
|
|
|
147
165
|
setParent(
|
|
@@ -155,97 +173,121 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
155
173
|
// avoid setting parents with empty `root`
|
|
156
174
|
if (!root) { return; }
|
|
157
175
|
|
|
158
|
-
|
|
176
|
+
const metadata: Metadata = this.ref.constructor[Symbol.metadata];
|
|
159
177
|
|
|
160
178
|
// skip if parent is already set
|
|
161
|
-
if (root
|
|
162
|
-
this.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
179
|
+
if (root !== this.root) {
|
|
180
|
+
this.root = root;
|
|
181
|
+
const isNewChangeTree = root.add(this);
|
|
182
|
+
|
|
183
|
+
if (root.types.hasFilters) {
|
|
184
|
+
this.checkIsFiltered(metadata, parent, parentIndex);
|
|
185
|
+
|
|
186
|
+
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
187
|
+
enqueueChangeTree(root, this, 'filteredChanges');
|
|
188
|
+
if (isNewChangeTree) {
|
|
189
|
+
this.root.allFilteredChanges.push(this);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
170
193
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
194
|
+
if (!this.isFiltered) {
|
|
195
|
+
enqueueChangeTree(root, this, 'changes');
|
|
196
|
+
if (isNewChangeTree) {
|
|
197
|
+
this.root.allChanges.push(this);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
174
200
|
|
|
175
|
-
if (this.isFiltered || this.isPartiallyFiltered) {
|
|
176
|
-
this.root.filteredChanges.set(this, this.filteredChanges);
|
|
177
|
-
this.root.allFilteredChanges.set(this, this.filteredChanges);
|
|
178
201
|
} else {
|
|
179
|
-
|
|
202
|
+
root.add(this);
|
|
180
203
|
}
|
|
181
204
|
|
|
182
|
-
|
|
205
|
+
// assign same parent on child structures
|
|
206
|
+
if (metadata) {
|
|
207
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
208
|
+
const field = metadata[index as any as number];
|
|
209
|
+
const value = this.ref[field.name];
|
|
210
|
+
value?.[$changes].setParent(this.ref, root, index);
|
|
211
|
+
|
|
212
|
+
// try { throw new Error(); } catch (e) {
|
|
213
|
+
// console.log(e.stack);
|
|
214
|
+
// }
|
|
215
|
+
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
} else if (this.ref[$childType] && typeof(this.ref[$childType]) !== "string") {
|
|
219
|
+
// MapSchema / ArraySchema, etc.
|
|
220
|
+
(this.ref as MapSchema).forEach((value, key) => {
|
|
221
|
+
value[$changes].setParent(this.ref, root, this.indexes[key] ?? key);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
183
224
|
|
|
184
|
-
this.forEachChild((changeTree, atIndex) => {
|
|
185
|
-
changeTree.setParent(this.ref, root, atIndex);
|
|
186
|
-
});
|
|
187
225
|
}
|
|
188
226
|
|
|
189
227
|
forEachChild(callback: (change: ChangeTree, atIndex: number) => void) {
|
|
190
228
|
//
|
|
191
229
|
// assign same parent on child structures
|
|
192
230
|
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (value && value[$changes]) {
|
|
201
|
-
callback(value[$changes], metadata[field].index);
|
|
231
|
+
const metadata: Metadata = this.ref.constructor[Symbol.metadata];
|
|
232
|
+
if (metadata) {
|
|
233
|
+
metadata[$refTypeFieldIndexes]?.forEach((index) => {
|
|
234
|
+
const field = metadata[index as any as number];
|
|
235
|
+
const value = this.ref[field.name];
|
|
236
|
+
if (value) {
|
|
237
|
+
callback(value[$changes], index);
|
|
202
238
|
}
|
|
203
|
-
}
|
|
239
|
+
});
|
|
204
240
|
|
|
205
|
-
} else if (typeof
|
|
241
|
+
} else if (this.ref[$childType] && typeof(this.ref[$childType]) !== "string") {
|
|
206
242
|
// MapSchema / ArraySchema, etc.
|
|
207
243
|
(this.ref as MapSchema).forEach((value, key) => {
|
|
208
|
-
|
|
209
|
-
callback(value[$changes], this.ref[$changes].indexes[key]);
|
|
210
|
-
}
|
|
244
|
+
callback(value[$changes], this.indexes[key] ?? key);
|
|
211
245
|
});
|
|
212
246
|
}
|
|
213
247
|
}
|
|
214
248
|
|
|
215
249
|
operation(op: OPERATION) {
|
|
216
|
-
|
|
217
|
-
this.
|
|
250
|
+
// operations without index use negative values to represent them
|
|
251
|
+
// this is checked during .encode() time.
|
|
252
|
+
this.changes.operations.push(-op);
|
|
253
|
+
|
|
254
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
218
255
|
}
|
|
219
256
|
|
|
220
257
|
change(index: number, operation: OPERATION = OPERATION.ADD) {
|
|
221
|
-
const metadata = this.ref
|
|
258
|
+
const metadata = this.ref.constructor[Symbol.metadata] as Metadata;
|
|
222
259
|
|
|
223
|
-
const isFiltered = this.isFiltered || (metadata
|
|
260
|
+
const isFiltered = this.isFiltered || (metadata?.[index]?.tag !== undefined);
|
|
224
261
|
const changeSet = (isFiltered)
|
|
225
262
|
? this.filteredChanges
|
|
226
263
|
: this.changes;
|
|
227
264
|
|
|
228
|
-
const previousOperation =
|
|
265
|
+
const previousOperation = this.indexedOperations[index];
|
|
229
266
|
if (!previousOperation || previousOperation === OPERATION.DELETE) {
|
|
230
267
|
const op = (!previousOperation)
|
|
231
268
|
? operation
|
|
232
269
|
: (previousOperation === OPERATION.DELETE)
|
|
233
270
|
? OPERATION.DELETE_AND_ADD
|
|
234
271
|
: operation
|
|
235
|
-
|
|
272
|
+
//
|
|
273
|
+
// TODO: are DELETE operations being encoded as ADD here ??
|
|
274
|
+
//
|
|
275
|
+
this.indexedOperations[index] = op;
|
|
236
276
|
}
|
|
237
277
|
|
|
238
|
-
|
|
239
|
-
// TODO: are DELETE operations being encoded as ADD here ??
|
|
240
|
-
//
|
|
278
|
+
setOperationAtIndex(changeSet, index);
|
|
241
279
|
|
|
242
280
|
if (isFiltered) {
|
|
243
|
-
this.allFilteredChanges
|
|
244
|
-
|
|
281
|
+
setOperationAtIndex(this.allFilteredChanges, index);
|
|
282
|
+
|
|
283
|
+
if (this.root) {
|
|
284
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
285
|
+
enqueueChangeTree(this.root, this, 'allFilteredChanges');
|
|
286
|
+
}
|
|
245
287
|
|
|
246
288
|
} else {
|
|
247
|
-
this.allChanges
|
|
248
|
-
this.root
|
|
289
|
+
setOperationAtIndex(this.allChanges, index);
|
|
290
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
249
291
|
}
|
|
250
292
|
}
|
|
251
293
|
|
|
@@ -259,13 +301,16 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
259
301
|
? this.filteredChanges
|
|
260
302
|
: this.changes;
|
|
261
303
|
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
changeSet.set(index + shiftIndex, op);
|
|
304
|
+
const newIndexedOperations = {};
|
|
305
|
+
const newIndexes = {};
|
|
306
|
+
for (const index in this.indexedOperations) {
|
|
307
|
+
newIndexedOperations[Number(index) + shiftIndex] = this.indexedOperations[index];
|
|
308
|
+
newIndexes[Number(index) + shiftIndex] = changeSet[index];
|
|
268
309
|
}
|
|
310
|
+
this.indexedOperations = newIndexedOperations;
|
|
311
|
+
changeSet.indexes = newIndexes;
|
|
312
|
+
|
|
313
|
+
changeSet.operations = changeSet.operations.map((index) => index + shiftIndex);
|
|
269
314
|
}
|
|
270
315
|
|
|
271
316
|
shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0) {
|
|
@@ -283,36 +328,46 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
283
328
|
}
|
|
284
329
|
}
|
|
285
330
|
|
|
286
|
-
private _shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
331
|
+
private _shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0, changeSet: ChangeSet) {
|
|
332
|
+
const newIndexes = {};
|
|
333
|
+
|
|
334
|
+
for (const key in changeSet.indexes) {
|
|
335
|
+
const index = changeSet.indexes[key];
|
|
336
|
+
if (index > startIndex) {
|
|
337
|
+
newIndexes[Number(key) + shiftIndex] = index;
|
|
338
|
+
} else {
|
|
339
|
+
newIndexes[key] = index;
|
|
292
340
|
}
|
|
293
|
-
}
|
|
341
|
+
}
|
|
342
|
+
changeSet.indexes = newIndexes;
|
|
343
|
+
|
|
344
|
+
for (let i = 0; i < changeSet.operations.length; i++) {
|
|
345
|
+
const index = changeSet.operations[i];
|
|
346
|
+
if (index > startIndex) {
|
|
347
|
+
changeSet.operations[i] = index + shiftIndex;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
294
350
|
}
|
|
295
351
|
|
|
296
|
-
indexedOperation(index: number, operation: OPERATION, allChangesIndex = index) {
|
|
297
|
-
|
|
298
|
-
const isFiltered = this.isFiltered || (metadata && metadata[metadata[index]].tag !== undefined);
|
|
352
|
+
indexedOperation(index: number, operation: OPERATION, allChangesIndex: number = index) {
|
|
353
|
+
this.indexedOperations[index] = operation;
|
|
299
354
|
|
|
300
|
-
if (
|
|
301
|
-
this.allFilteredChanges
|
|
302
|
-
this.filteredChanges
|
|
303
|
-
this.root
|
|
355
|
+
if (this.filteredChanges) {
|
|
356
|
+
setOperationAtIndex(this.allFilteredChanges, allChangesIndex);
|
|
357
|
+
setOperationAtIndex(this.filteredChanges, index);
|
|
358
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
304
359
|
|
|
305
360
|
} else {
|
|
306
|
-
this.allChanges
|
|
307
|
-
this.changes
|
|
308
|
-
this.root
|
|
361
|
+
setOperationAtIndex(this.allChanges, allChangesIndex);
|
|
362
|
+
setOperationAtIndex(this.changes, index);
|
|
363
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
309
364
|
}
|
|
310
365
|
}
|
|
311
366
|
|
|
312
367
|
getType(index?: number) {
|
|
313
368
|
if (Metadata.isValidInstance(this.ref)) {
|
|
314
|
-
const metadata = this.ref
|
|
315
|
-
return metadata[
|
|
369
|
+
const metadata = this.ref.constructor[Symbol.metadata] as Metadata;
|
|
370
|
+
return metadata[index].type;
|
|
316
371
|
|
|
317
372
|
} else {
|
|
318
373
|
//
|
|
@@ -326,8 +381,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
326
381
|
}
|
|
327
382
|
|
|
328
383
|
getChange(index: number) {
|
|
329
|
-
|
|
330
|
-
return this.changes.get(index) ?? this.filteredChanges.get(index);
|
|
384
|
+
return this.indexedOperations[index];
|
|
331
385
|
}
|
|
332
386
|
|
|
333
387
|
//
|
|
@@ -350,20 +404,17 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
350
404
|
return;
|
|
351
405
|
}
|
|
352
406
|
|
|
353
|
-
const
|
|
354
|
-
const isFiltered = this.isFiltered || (metadata && metadata[metadata[index]].tag !== undefined);
|
|
355
|
-
const changeSet = (isFiltered)
|
|
407
|
+
const changeSet = (this.filteredChanges)
|
|
356
408
|
? this.filteredChanges
|
|
357
409
|
: this.changes;
|
|
358
410
|
|
|
359
|
-
|
|
411
|
+
this.indexedOperations[index] = operation ?? OPERATION.DELETE;
|
|
412
|
+
setOperationAtIndex(changeSet, index);
|
|
360
413
|
|
|
361
|
-
|
|
414
|
+
const previousValue = this.getValue(index);
|
|
362
415
|
|
|
363
416
|
// remove `root` reference
|
|
364
417
|
if (previousValue && previousValue[$changes]) {
|
|
365
|
-
previousValue[$changes].root = undefined;
|
|
366
|
-
|
|
367
418
|
//
|
|
368
419
|
// FIXME: this.root is "undefined"
|
|
369
420
|
//
|
|
@@ -378,24 +429,30 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
378
429
|
}
|
|
379
430
|
|
|
380
431
|
//
|
|
381
|
-
// FIXME: this is looking a
|
|
432
|
+
// FIXME: this is looking a ugly and repeated
|
|
382
433
|
//
|
|
383
|
-
if (
|
|
384
|
-
this.
|
|
385
|
-
this.
|
|
434
|
+
if (this.filteredChanges) {
|
|
435
|
+
deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
|
|
436
|
+
enqueueChangeTree(this.root, this, 'filteredChanges');
|
|
386
437
|
|
|
387
438
|
} else {
|
|
388
|
-
this.
|
|
389
|
-
this.
|
|
439
|
+
deleteOperationAtIndex(this.allChanges, allChangesIndex);
|
|
440
|
+
enqueueChangeTree(this.root, this, 'changes');
|
|
390
441
|
}
|
|
391
442
|
}
|
|
392
443
|
|
|
393
444
|
endEncode() {
|
|
394
|
-
this.
|
|
445
|
+
this.indexedOperations = {};
|
|
446
|
+
|
|
447
|
+
// // clear changes
|
|
448
|
+
// this.changes.indexes = {};
|
|
449
|
+
// this.changes.operations.length = 0;
|
|
450
|
+
|
|
451
|
+
// ArraySchema and MapSchema have a custom "encode end" method
|
|
395
452
|
this.ref[$onEncodeEnd]?.();
|
|
396
453
|
|
|
397
454
|
// Not a new instance anymore
|
|
398
|
-
|
|
455
|
+
this.isNew = false;
|
|
399
456
|
}
|
|
400
457
|
|
|
401
458
|
discard(discardAll: boolean = false) {
|
|
@@ -406,15 +463,26 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
406
463
|
//
|
|
407
464
|
this.ref[$onEncodeEnd]?.();
|
|
408
465
|
|
|
409
|
-
this.
|
|
410
|
-
this.filteredChanges.clear();
|
|
466
|
+
this.indexedOperations = {};
|
|
411
467
|
|
|
412
|
-
|
|
413
|
-
this.
|
|
468
|
+
this.changes.indexes = {};
|
|
469
|
+
this.changes.operations.length = 0;
|
|
470
|
+
this.changes.queueRootIndex = undefined;
|
|
471
|
+
|
|
472
|
+
if (this.filteredChanges !== undefined) {
|
|
473
|
+
this.filteredChanges.indexes = {};
|
|
474
|
+
this.filteredChanges.operations.length = 0;
|
|
475
|
+
this.filteredChanges.queueRootIndex = undefined;
|
|
476
|
+
}
|
|
414
477
|
|
|
415
478
|
if (discardAll) {
|
|
416
|
-
this.allChanges.
|
|
417
|
-
this.
|
|
479
|
+
this.allChanges.indexes = {};
|
|
480
|
+
this.allChanges.operations.length = 0;
|
|
481
|
+
|
|
482
|
+
if (this.allFilteredChanges !== undefined) {
|
|
483
|
+
this.allFilteredChanges.indexes = {};
|
|
484
|
+
this.allFilteredChanges.operations.length = 0;
|
|
485
|
+
}
|
|
418
486
|
|
|
419
487
|
// remove children references
|
|
420
488
|
this.forEachChild((changeTree, _) =>
|
|
@@ -426,13 +494,14 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
426
494
|
* Recursively discard all changes from this, and child structures.
|
|
427
495
|
*/
|
|
428
496
|
discardAll() {
|
|
429
|
-
this.
|
|
430
|
-
|
|
497
|
+
const keys = Object.keys(this.indexedOperations);
|
|
498
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
499
|
+
const value = this.getValue(Number(keys[i]));
|
|
431
500
|
|
|
432
501
|
if (value && value[$changes]) {
|
|
433
502
|
value[$changes].discardAll();
|
|
434
503
|
}
|
|
435
|
-
}
|
|
504
|
+
}
|
|
436
505
|
|
|
437
506
|
this.discard();
|
|
438
507
|
}
|
|
@@ -447,26 +516,31 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
447
516
|
}
|
|
448
517
|
|
|
449
518
|
get changed() {
|
|
450
|
-
return this.
|
|
519
|
+
return (Object.entries(this.indexedOperations).length > 0);
|
|
451
520
|
}
|
|
452
521
|
|
|
453
|
-
protected checkIsFiltered(parent: Ref, parentIndex: number) {
|
|
522
|
+
protected checkIsFiltered(metadata: Metadata, parent: Ref, parentIndex: number) {
|
|
454
523
|
// Detect if current structure has "filters" declared
|
|
455
|
-
this.isPartiallyFiltered =
|
|
524
|
+
this.isPartiallyFiltered = metadata?.[$viewFieldIndexes] !== undefined;
|
|
456
525
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const metadata: Metadata = parent['constructor'][Symbol.metadata];
|
|
526
|
+
if (this.isPartiallyFiltered) {
|
|
527
|
+
this.filteredChanges = this.filteredChanges || { indexes: {}, operations: [] };
|
|
528
|
+
this.allFilteredChanges = this.allFilteredChanges || { indexes: {}, operations: [] };
|
|
529
|
+
}
|
|
462
530
|
|
|
463
|
-
|
|
464
|
-
|
|
531
|
+
// skip if parent is not set
|
|
532
|
+
if (!parent) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
465
535
|
|
|
466
|
-
|
|
536
|
+
if (!Metadata.isValidInstance(parent)) {
|
|
537
|
+
const parentChangeTree = parent[$changes];
|
|
538
|
+
parent = parentChangeTree.parent;
|
|
539
|
+
parentIndex = parentChangeTree.parentIndex;
|
|
540
|
+
}
|
|
467
541
|
|
|
468
|
-
|
|
469
|
-
|
|
542
|
+
const parentMetadata = parent.constructor?.[Symbol.metadata];
|
|
543
|
+
this.isFiltered = parentMetadata?.[$viewFieldIndexes]?.includes(parentIndex);
|
|
470
544
|
|
|
471
545
|
//
|
|
472
546
|
// TODO: refactor this!
|
|
@@ -474,16 +548,26 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
474
548
|
// swapping `changes` and `filteredChanges` is required here
|
|
475
549
|
// because "isFiltered" may not be imedialely available on `change()`
|
|
476
550
|
//
|
|
477
|
-
if (this.isFiltered
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
this.
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
551
|
+
if (this.isFiltered) {
|
|
552
|
+
this.filteredChanges = { indexes: {}, operations: [] };
|
|
553
|
+
this.allFilteredChanges = { indexes: {}, operations: [] };
|
|
554
|
+
|
|
555
|
+
if (this.changes.operations.length > 0) {
|
|
556
|
+
// swap changes reference
|
|
557
|
+
const changes = this.changes;
|
|
558
|
+
this.changes = this.filteredChanges;
|
|
559
|
+
this.filteredChanges = changes;
|
|
560
|
+
|
|
561
|
+
// swap "all changes" reference
|
|
562
|
+
const allFilteredChanges = this.allFilteredChanges;
|
|
563
|
+
this.allFilteredChanges = this.allChanges;
|
|
564
|
+
this.allChanges = allFilteredChanges;
|
|
565
|
+
|
|
566
|
+
// console.log("SWAP =>", {
|
|
567
|
+
// "this.allFilteredChanges": this.allFilteredChanges,
|
|
568
|
+
// "this.allChanges": this.allChanges
|
|
569
|
+
// })
|
|
570
|
+
}
|
|
487
571
|
}
|
|
488
572
|
}
|
|
489
573
|
|