@colyseus/schema 3.0.0-alpha.4 → 3.0.0-alpha.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -61
- package/bin/schema-debug +94 -0
- package/build/cjs/index.js +1521 -809
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +1519 -808
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +1528 -816
- 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 +1 -2
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +1 -2
- package/lib/codegen/languages/haxe.js.map +1 -1
- package/lib/codegen/languages/java.js +1 -2
- package/lib/codegen/languages/java.js.map +1 -1
- package/lib/codegen/languages/js.js +1 -2
- package/lib/codegen/languages/js.js.map +1 -1
- package/lib/codegen/languages/lua.js +1 -2
- package/lib/codegen/languages/lua.js.map +1 -1
- package/lib/codegen/languages/ts.js +1 -2
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +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 +0 -1
- package/lib/decoder/DecodeOperation.js +30 -12
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +6 -7
- package/lib/decoder/Decoder.js +9 -9
- 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 +47 -61
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +9 -8
- package/lib/encoder/Encoder.js +165 -88
- 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 +2 -1
- package/lib/encoding/assert.js +5 -5
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.js +20 -21
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +2 -2
- package/lib/encoding/encode.js +52 -48
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +2 -1
- package/lib/encoding/spec.js +1 -0
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +6 -3
- package/lib/index.js +19 -13
- 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.js +3 -4
- 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 +155 -203
- package/src/bench_encode.ts +108 -0
- 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 +40 -12
- package/src/decoder/Decoder.ts +14 -12
- 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 +77 -77
- package/src/encoder/Encoder.ts +201 -96
- package/src/encoder/Root.ts +93 -0
- package/src/encoder/StateView.ts +76 -88
- package/src/encoding/assert.ts +4 -3
- package/src/encoding/encode.ts +37 -31
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +8 -14
- 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/symbols.ts +10 -7
- package/src/utils.ts +7 -3
package/src/Metadata.ts
CHANGED
|
@@ -1,134 +1,282 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Definition, DefinitionType, getPropertyDescriptor } from "./annotations";
|
|
2
|
+
import { Schema } from "./Schema";
|
|
2
3
|
import { getType } from "./types/registry";
|
|
4
|
+
import { $decoder, $descriptors, $encoder, $fieldIndexesByViewTag, $numFields, $refTypeFieldIndexes, $track, $viewFieldIndexes } from "./types/symbols";
|
|
5
|
+
import { TypeContext } from "./types/TypeContext";
|
|
3
6
|
|
|
4
7
|
export type MetadataField = {
|
|
5
8
|
type: DefinitionType,
|
|
9
|
+
name: string,
|
|
6
10
|
index: number,
|
|
7
11
|
tag?: number,
|
|
8
12
|
unreliable?: boolean,
|
|
9
13
|
deprecated?: boolean,
|
|
10
|
-
descriptor?: PropertyDescriptor,
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
export type Metadata =
|
|
14
|
-
{ [
|
|
15
|
-
{ [
|
|
16
|
-
{ [
|
|
17
|
-
{ [
|
|
18
|
-
{ [field:
|
|
17
|
+
{ [$numFields]: number; } & // number of fields
|
|
18
|
+
{ [$viewFieldIndexes]: number[]; } & // all field indexes with "view" tag
|
|
19
|
+
{ [$fieldIndexesByViewTag]: {[tag: number]: number[]}; } & // field indexes by "view" tag
|
|
20
|
+
{ [$refTypeFieldIndexes]: number[]; } & // all field indexes containing Ref types (Schema, ArraySchema, MapSchema, etc)
|
|
21
|
+
{ [field: number]: MetadataField; } & // index => field name
|
|
22
|
+
{ [field: string]: number; } & // field name => field metadata
|
|
23
|
+
{ [$descriptors]: { [field: string]: PropertyDescriptor } } // property descriptors
|
|
24
|
+
|
|
25
|
+
export function getNormalizedType(type: DefinitionType): DefinitionType {
|
|
26
|
+
return (Array.isArray(type))
|
|
27
|
+
? { array: type[0] }
|
|
28
|
+
: (typeof(type['type']) !== "undefined")
|
|
29
|
+
? type['type']
|
|
30
|
+
: type;
|
|
31
|
+
}
|
|
19
32
|
|
|
20
33
|
export const Metadata = {
|
|
21
34
|
|
|
22
|
-
addField(metadata: any, index: number,
|
|
35
|
+
addField(metadata: any, index: number, name: string, type: DefinitionType, descriptor?: PropertyDescriptor) {
|
|
23
36
|
if (index > 64) {
|
|
24
|
-
throw new Error(`Can't define field '${
|
|
37
|
+
throw new Error(`Can't define field '${name}'.\nSchema instances may only have up to 64 fields.`);
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
metadata[
|
|
28
|
-
metadata[
|
|
40
|
+
metadata[index] = Object.assign(
|
|
41
|
+
metadata[index] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
|
|
29
42
|
{
|
|
30
|
-
type: (
|
|
31
|
-
? { array: type[0] }
|
|
32
|
-
: type,
|
|
43
|
+
type: getNormalizedType(type),
|
|
33
44
|
index,
|
|
34
|
-
|
|
45
|
+
name,
|
|
35
46
|
}
|
|
36
47
|
);
|
|
37
48
|
|
|
49
|
+
// create "descriptors" map
|
|
50
|
+
Object.defineProperty(metadata, $descriptors, {
|
|
51
|
+
value: metadata[$descriptors] || {},
|
|
52
|
+
enumerable: false,
|
|
53
|
+
configurable: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (descriptor) {
|
|
57
|
+
// for encoder
|
|
58
|
+
metadata[$descriptors][name] = descriptor;
|
|
59
|
+
metadata[$descriptors][`_${name}`] = {
|
|
60
|
+
value: undefined,
|
|
61
|
+
writable: true,
|
|
62
|
+
enumerable: false,
|
|
63
|
+
configurable: true,
|
|
64
|
+
};
|
|
65
|
+
} else {
|
|
66
|
+
// for decoder
|
|
67
|
+
metadata[$descriptors][name] = {
|
|
68
|
+
value: undefined,
|
|
69
|
+
writable: true,
|
|
70
|
+
enumerable: true,
|
|
71
|
+
configurable: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
38
75
|
// map -1 as last field index
|
|
39
|
-
Object.defineProperty(metadata,
|
|
76
|
+
Object.defineProperty(metadata, $numFields, {
|
|
40
77
|
value: index,
|
|
41
78
|
enumerable: false,
|
|
42
79
|
configurable: true
|
|
43
80
|
});
|
|
44
81
|
|
|
45
|
-
// map
|
|
46
|
-
Object.defineProperty(metadata,
|
|
47
|
-
value:
|
|
82
|
+
// map field name => index (non enumerable)
|
|
83
|
+
Object.defineProperty(metadata, name, {
|
|
84
|
+
value: index,
|
|
48
85
|
enumerable: false,
|
|
49
86
|
configurable: true,
|
|
50
87
|
});
|
|
88
|
+
|
|
89
|
+
// if child Ref/complex type, add to -4
|
|
90
|
+
if (typeof (metadata[index].type) !== "string") {
|
|
91
|
+
if (metadata[$refTypeFieldIndexes] === undefined) {
|
|
92
|
+
Object.defineProperty(metadata, $refTypeFieldIndexes, {
|
|
93
|
+
value: [],
|
|
94
|
+
enumerable: false,
|
|
95
|
+
configurable: true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
metadata[$refTypeFieldIndexes].push(index);
|
|
99
|
+
}
|
|
51
100
|
},
|
|
52
101
|
|
|
53
102
|
setTag(metadata: Metadata, fieldName: string, tag: number) {
|
|
103
|
+
const index = metadata[fieldName];
|
|
104
|
+
const field = metadata[index];
|
|
105
|
+
|
|
54
106
|
// add 'tag' to the field
|
|
55
|
-
const field = metadata[fieldName];
|
|
56
107
|
field.tag = tag;
|
|
57
108
|
|
|
58
|
-
if (!metadata[
|
|
109
|
+
if (!metadata[$viewFieldIndexes]) {
|
|
59
110
|
// -2: all field indexes with "view" tag
|
|
60
|
-
Object.defineProperty(metadata,
|
|
111
|
+
Object.defineProperty(metadata, $viewFieldIndexes, {
|
|
61
112
|
value: [],
|
|
62
113
|
enumerable: false,
|
|
63
114
|
configurable: true
|
|
64
115
|
});
|
|
65
116
|
|
|
66
117
|
// -3: field indexes by "view" tag
|
|
67
|
-
Object.defineProperty(metadata,
|
|
118
|
+
Object.defineProperty(metadata, $fieldIndexesByViewTag, {
|
|
68
119
|
value: {},
|
|
69
120
|
enumerable: false,
|
|
70
121
|
configurable: true
|
|
71
122
|
});
|
|
72
123
|
}
|
|
73
124
|
|
|
74
|
-
metadata[
|
|
125
|
+
metadata[$viewFieldIndexes].push(index);
|
|
75
126
|
|
|
76
|
-
if (!metadata[
|
|
77
|
-
metadata[
|
|
127
|
+
if (!metadata[$fieldIndexesByViewTag][tag]) {
|
|
128
|
+
metadata[$fieldIndexesByViewTag][tag] = [];
|
|
78
129
|
}
|
|
79
130
|
|
|
80
|
-
metadata[
|
|
131
|
+
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
81
132
|
},
|
|
82
133
|
|
|
83
134
|
setFields(target: any, fields: { [field: string]: DefinitionType }) {
|
|
84
|
-
|
|
135
|
+
// for inheritance support
|
|
136
|
+
const constructor = target.prototype.constructor;
|
|
137
|
+
TypeContext.register(constructor);
|
|
138
|
+
|
|
139
|
+
const parentClass = Object.getPrototypeOf(constructor);
|
|
140
|
+
const parentMetadata = parentClass && parentClass[Symbol.metadata];
|
|
141
|
+
const metadata = Metadata.initialize(constructor);
|
|
85
142
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
143
|
+
// Use Schema's methods if not defined in the class
|
|
144
|
+
if (!constructor[$track]) { constructor[$track] = Schema[$track]; }
|
|
145
|
+
if (!constructor[$encoder]) { constructor[$encoder] = Schema[$encoder]; }
|
|
146
|
+
if (!constructor[$decoder]) { constructor[$decoder] = Schema[$decoder]; }
|
|
147
|
+
if (!constructor.prototype.toJSON) { constructor.prototype.toJSON = Schema.prototype.toJSON; }
|
|
89
148
|
|
|
90
|
-
//
|
|
91
|
-
//
|
|
149
|
+
//
|
|
150
|
+
// detect index for this field, considering inheritance
|
|
151
|
+
//
|
|
152
|
+
let fieldIndex = metadata[$numFields] // current structure already has fields defined
|
|
153
|
+
?? (parentMetadata && parentMetadata[$numFields]) // parent structure has fields defined
|
|
154
|
+
?? -1; // no fields defined
|
|
92
155
|
|
|
93
|
-
|
|
156
|
+
fieldIndex++;
|
|
94
157
|
|
|
95
|
-
let index = 0;
|
|
96
158
|
for (const field in fields) {
|
|
97
159
|
const type = fields[field];
|
|
160
|
+
const normalizedType = getNormalizedType(type);
|
|
98
161
|
|
|
99
162
|
// FIXME: this code is duplicated from @type() annotation
|
|
100
163
|
const complexTypeKlass = (Array.isArray(type))
|
|
101
164
|
? getType("array")
|
|
102
165
|
: (typeof(Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
|
|
103
166
|
|
|
167
|
+
const childType = (complexTypeKlass)
|
|
168
|
+
? Object.values(type)[0]
|
|
169
|
+
: normalizedType;
|
|
170
|
+
|
|
104
171
|
Metadata.addField(
|
|
105
172
|
metadata,
|
|
106
|
-
|
|
173
|
+
fieldIndex,
|
|
107
174
|
field,
|
|
108
175
|
type,
|
|
109
|
-
getPropertyDescriptor(`_${field}`,
|
|
176
|
+
getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass)
|
|
110
177
|
);
|
|
111
178
|
|
|
112
|
-
|
|
179
|
+
fieldIndex++;
|
|
113
180
|
}
|
|
181
|
+
|
|
182
|
+
return target;
|
|
114
183
|
},
|
|
115
184
|
|
|
116
185
|
isDeprecated(metadata: any, field: string) {
|
|
117
186
|
return metadata[field].deprecated === true;
|
|
118
187
|
},
|
|
119
188
|
|
|
189
|
+
init(klass: any) {
|
|
190
|
+
//
|
|
191
|
+
// Used only to initialize an empty Schema (Encoder#constructor)
|
|
192
|
+
// TODO: remove/refactor this...
|
|
193
|
+
//
|
|
194
|
+
const metadata = {};
|
|
195
|
+
klass[Symbol.metadata] = metadata;
|
|
196
|
+
Object.defineProperty(metadata, $numFields, {
|
|
197
|
+
value: 0,
|
|
198
|
+
enumerable: false,
|
|
199
|
+
configurable: true,
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
initialize(constructor: any) {
|
|
204
|
+
const parentClass = Object.getPrototypeOf(constructor);
|
|
205
|
+
const parentMetadata: Metadata = parentClass[Symbol.metadata];
|
|
206
|
+
|
|
207
|
+
let metadata: Metadata = constructor[Symbol.metadata] ?? Object.create(null);
|
|
208
|
+
|
|
209
|
+
// make sure inherited classes have their own metadata object.
|
|
210
|
+
if (parentClass !== Schema && metadata === parentMetadata) {
|
|
211
|
+
metadata = Object.create(null);
|
|
212
|
+
|
|
213
|
+
if (parentMetadata) {
|
|
214
|
+
//
|
|
215
|
+
// assign parent metadata to current
|
|
216
|
+
//
|
|
217
|
+
Object.setPrototypeOf(metadata, parentMetadata);
|
|
218
|
+
|
|
219
|
+
// $numFields
|
|
220
|
+
Object.defineProperty(metadata, $numFields, {
|
|
221
|
+
value: parentMetadata[$numFields],
|
|
222
|
+
enumerable: false,
|
|
223
|
+
configurable: true,
|
|
224
|
+
writable: true,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// $viewFieldIndexes / $fieldIndexesByViewTag
|
|
228
|
+
if (parentMetadata[$viewFieldIndexes] !== undefined) {
|
|
229
|
+
Object.defineProperty(metadata, $viewFieldIndexes, {
|
|
230
|
+
value: [...parentMetadata[$viewFieldIndexes]],
|
|
231
|
+
enumerable: false,
|
|
232
|
+
configurable: true,
|
|
233
|
+
writable: true,
|
|
234
|
+
});
|
|
235
|
+
Object.defineProperty(metadata, $fieldIndexesByViewTag, {
|
|
236
|
+
value: { ...parentMetadata[$fieldIndexesByViewTag] },
|
|
237
|
+
enumerable: false,
|
|
238
|
+
configurable: true,
|
|
239
|
+
writable: true,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// $refTypeFieldIndexes
|
|
244
|
+
if (parentMetadata[$refTypeFieldIndexes] !== undefined) {
|
|
245
|
+
Object.defineProperty(metadata, $refTypeFieldIndexes, {
|
|
246
|
+
value: [...parentMetadata[$refTypeFieldIndexes]],
|
|
247
|
+
enumerable: false,
|
|
248
|
+
configurable: true,
|
|
249
|
+
writable: true,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// $descriptors
|
|
254
|
+
Object.defineProperty(metadata, $descriptors, {
|
|
255
|
+
value: { ...parentMetadata[$descriptors] },
|
|
256
|
+
enumerable: false,
|
|
257
|
+
configurable: true,
|
|
258
|
+
writable: true,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
constructor[Symbol.metadata] = metadata;
|
|
264
|
+
|
|
265
|
+
return metadata;
|
|
266
|
+
},
|
|
267
|
+
|
|
120
268
|
isValidInstance(klass: any) {
|
|
121
269
|
return (
|
|
122
270
|
klass.constructor[Symbol.metadata] &&
|
|
123
|
-
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata],
|
|
271
|
+
Object.prototype.hasOwnProperty.call(klass.constructor[Symbol.metadata], $numFields) as boolean
|
|
124
272
|
);
|
|
125
273
|
},
|
|
126
274
|
|
|
127
275
|
getFields(klass: any) {
|
|
128
|
-
const metadata = klass[Symbol.metadata];
|
|
276
|
+
const metadata: Metadata = klass[Symbol.metadata];
|
|
129
277
|
const fields = {};
|
|
130
|
-
for (let i = 0; i <= metadata[
|
|
131
|
-
fields[metadata[i]] = metadata[
|
|
278
|
+
for (let i = 0; i <= metadata[$numFields]; i++) {
|
|
279
|
+
fields[metadata[i].name] = metadata[i].type;
|
|
132
280
|
}
|
|
133
281
|
return fields;
|
|
134
282
|
}
|
package/src/Reflection.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { type, PrimitiveType, DefinitionType
|
|
1
|
+
import { type, PrimitiveType, DefinitionType } from "./annotations";
|
|
2
|
+
import { TypeContext } from "./types/TypeContext";
|
|
2
3
|
import { Metadata } from "./Metadata";
|
|
3
4
|
import { ArraySchema } from "./types/custom/ArraySchema";
|
|
4
5
|
import { Iterator } from "./encoding/decode";
|
|
5
6
|
import { Encoder } from "./encoder/Encoder";
|
|
6
7
|
import { Decoder } from "./decoder/Decoder";
|
|
7
8
|
import { Schema } from "./Schema";
|
|
9
|
+
import { $numFields } from "./types/symbols";
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Reflection
|
|
@@ -22,18 +24,32 @@ export class ReflectionType extends Schema {
|
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export class Reflection extends Schema {
|
|
25
|
-
@type([
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
@type([ReflectionType]) types: ArraySchema<ReflectionType> = new ArraySchema<ReflectionType>();
|
|
28
|
+
@type("number") rootType: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Encodes the TypeContext of an Encoder into a buffer.
|
|
32
|
+
*
|
|
33
|
+
* @param encoder Encoder instance
|
|
34
|
+
* @param it
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
37
|
+
static encode(encoder: Encoder, it: Iterator = { offset: 0 }) {
|
|
38
|
+
const context = encoder.context;
|
|
31
39
|
|
|
32
40
|
const reflection = new Reflection();
|
|
33
|
-
const
|
|
41
|
+
const reflectionEncoder = new Encoder(reflection);
|
|
42
|
+
|
|
43
|
+
// rootType is usually the first schema passed to the Encoder
|
|
44
|
+
// (unless it inherits from another schema)
|
|
45
|
+
const rootType = context.schemas.get(encoder.state.constructor);
|
|
46
|
+
if (rootType > 0) { reflection.rootType = rootType; }
|
|
34
47
|
|
|
35
48
|
const buildType = (currentType: ReflectionType, metadata: Metadata) => {
|
|
36
|
-
for (const
|
|
49
|
+
for (const fieldIndex in metadata) {
|
|
50
|
+
const index = Number(fieldIndex);
|
|
51
|
+
const fieldName = metadata[index].name;
|
|
52
|
+
|
|
37
53
|
// skip fields from parent classes
|
|
38
54
|
if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
|
|
39
55
|
continue;
|
|
@@ -44,7 +60,7 @@ export class Reflection extends Schema {
|
|
|
44
60
|
|
|
45
61
|
let fieldType: string;
|
|
46
62
|
|
|
47
|
-
const type = metadata[
|
|
63
|
+
const type = metadata[index].type;
|
|
48
64
|
|
|
49
65
|
if (typeof (type) === "string") {
|
|
50
66
|
fieldType = type;
|
|
@@ -96,73 +112,95 @@ export class Reflection extends Schema {
|
|
|
96
112
|
buildType(type, klass[Symbol.metadata]);
|
|
97
113
|
}
|
|
98
114
|
|
|
99
|
-
const
|
|
100
|
-
const buf = encoder.encodeAll(it);
|
|
115
|
+
const buf = reflectionEncoder.encodeAll(it);
|
|
101
116
|
return Buffer.from(buf, 0, it.offset);
|
|
102
117
|
}
|
|
103
118
|
|
|
104
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Decodes the TypeContext from a buffer into a Decoder instance.
|
|
121
|
+
*
|
|
122
|
+
* @param bytes Reflection.encode() output
|
|
123
|
+
* @param it
|
|
124
|
+
* @returns Decoder instance
|
|
125
|
+
*/
|
|
126
|
+
static decode<T extends Schema = Schema>(bytes: Buffer, it?: Iterator): Decoder<T> {
|
|
105
127
|
const reflection = new Reflection();
|
|
106
128
|
|
|
107
129
|
const reflectionDecoder = new Decoder(reflection);
|
|
108
130
|
reflectionDecoder.decode(bytes, it);
|
|
109
131
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
const schemaTypes = reflection.types.reduce((types, reflectionType) => {
|
|
113
|
-
const parentKlass: typeof Schema = types[reflectionType.extendsId] || Schema;
|
|
114
|
-
const schema: typeof Schema = class _ extends parentKlass {};
|
|
132
|
+
const typeContext = new TypeContext();
|
|
115
133
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
134
|
+
// 1st pass, initialize metadata + inheritance
|
|
135
|
+
reflection.types.forEach((reflectionType) => {
|
|
136
|
+
const parentClass: typeof Schema = typeContext.get(reflectionType.extendsId) ?? Schema;
|
|
137
|
+
const schema: typeof Schema = class _ extends parentClass {};
|
|
119
138
|
|
|
120
139
|
// register for inheritance support
|
|
121
140
|
TypeContext.register(schema);
|
|
122
141
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
context.add(schema, typeid);
|
|
126
|
-
return types;
|
|
127
|
-
}, {});
|
|
142
|
+
// // for inheritance support
|
|
143
|
+
// Metadata.initialize(schema);
|
|
128
144
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const metadata = schemaType[Symbol.metadata];
|
|
132
|
-
|
|
133
|
-
const parentKlass = reflection.types[reflectionType.extendsId];
|
|
134
|
-
const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
|
|
145
|
+
typeContext.add(schema, reflectionType.id);
|
|
146
|
+
}, {});
|
|
135
147
|
|
|
148
|
+
// define fields
|
|
149
|
+
const addFields = (metadata: Metadata, reflectionType: ReflectionType, parentFieldIndex: number) => {
|
|
136
150
|
reflectionType.fields.forEach((field, i) => {
|
|
137
151
|
const fieldIndex = parentFieldIndex + i;
|
|
138
152
|
|
|
139
153
|
if (field.referencedType !== undefined) {
|
|
140
154
|
let fieldType = field.type;
|
|
141
|
-
let refType =
|
|
155
|
+
let refType: PrimitiveType = typeContext.get(field.referencedType);
|
|
142
156
|
|
|
143
157
|
// map or array of primitive type (-1)
|
|
144
158
|
if (!refType) {
|
|
145
159
|
const typeInfo = field.type.split(":");
|
|
146
160
|
fieldType = typeInfo[0];
|
|
147
|
-
refType = typeInfo[1];
|
|
161
|
+
refType = typeInfo[1] as PrimitiveType; // string
|
|
148
162
|
}
|
|
149
163
|
|
|
150
164
|
if (fieldType === "ref") {
|
|
151
|
-
// type(refType)(schemaType.prototype, field.name);
|
|
152
165
|
Metadata.addField(metadata, fieldIndex, field.name, refType);
|
|
153
166
|
|
|
154
167
|
} else {
|
|
155
|
-
|
|
156
|
-
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType } as DefinitionType);
|
|
168
|
+
Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
} else {
|
|
160
|
-
// type(field.type as PrimitiveType)(schemaType.prototype, field.name);
|
|
161
172
|
Metadata.addField(metadata, fieldIndex, field.name, field.type as PrimitiveType);
|
|
162
173
|
}
|
|
163
174
|
});
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// 2nd pass, set fields
|
|
178
|
+
reflection.types.forEach((reflectionType) => {
|
|
179
|
+
const schema = typeContext.get(reflectionType.id);
|
|
180
|
+
|
|
181
|
+
// for inheritance support
|
|
182
|
+
const metadata = Metadata.initialize(schema);
|
|
183
|
+
|
|
184
|
+
const inheritedTypes: ReflectionType[] = [];
|
|
185
|
+
|
|
186
|
+
let parentType: ReflectionType = reflectionType;
|
|
187
|
+
do {
|
|
188
|
+
inheritedTypes.push(parentType);
|
|
189
|
+
parentType = reflection.types.find((t) => t.id === parentType.extendsId);
|
|
190
|
+
} while (parentType);
|
|
191
|
+
|
|
192
|
+
let parentFieldIndex = 0;
|
|
193
|
+
|
|
194
|
+
inheritedTypes.reverse().forEach((reflectionType) => {
|
|
195
|
+
// add fields from all inherited classes
|
|
196
|
+
// TODO: refactor this to avoid adding fields from parent classes
|
|
197
|
+
addFields(metadata, reflectionType, parentFieldIndex);
|
|
198
|
+
parentFieldIndex += reflectionType.fields.length;
|
|
199
|
+
});
|
|
164
200
|
});
|
|
165
201
|
|
|
166
|
-
|
|
202
|
+
const state: T = new (typeContext.get(reflection.rootType || 0) as unknown as any)();
|
|
203
|
+
|
|
204
|
+
return new Decoder<T>(state, typeContext);
|
|
167
205
|
}
|
|
168
206
|
}
|