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