@colyseus/schema 3.0.0-alpha.9 → 3.0.1

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.
Files changed (202) hide show
  1. package/README.md +148 -62
  2. package/bin/schema-debug +94 -0
  3. package/build/cjs/index.js +2222 -1513
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/esm/index.mjs +2223 -1516
  6. package/build/esm/index.mjs.map +1 -1
  7. package/build/umd/index.js +2225 -1516
  8. package/lib/Metadata.d.ts +21 -9
  9. package/lib/Metadata.js +169 -32
  10. package/lib/Metadata.js.map +1 -1
  11. package/lib/Reflection.d.ts +19 -4
  12. package/lib/Reflection.js +66 -31
  13. package/lib/Reflection.js.map +1 -1
  14. package/lib/Schema.d.ts +12 -5
  15. package/lib/Schema.js +57 -56
  16. package/lib/Schema.js.map +1 -1
  17. package/lib/annotations.d.ts +31 -34
  18. package/lib/annotations.js +110 -160
  19. package/lib/annotations.js.map +1 -1
  20. package/lib/codegen/api.js +1 -2
  21. package/lib/codegen/api.js.map +1 -1
  22. package/lib/codegen/languages/cpp.js +1 -2
  23. package/lib/codegen/languages/cpp.js.map +1 -1
  24. package/lib/codegen/languages/csharp.js +9 -46
  25. package/lib/codegen/languages/csharp.js.map +1 -1
  26. package/lib/codegen/languages/haxe.js +4 -2
  27. package/lib/codegen/languages/haxe.js.map +1 -1
  28. package/lib/codegen/languages/java.js +1 -2
  29. package/lib/codegen/languages/java.js.map +1 -1
  30. package/lib/codegen/languages/js.js +1 -2
  31. package/lib/codegen/languages/js.js.map +1 -1
  32. package/lib/codegen/languages/lua.js +23 -25
  33. package/lib/codegen/languages/lua.js.map +1 -1
  34. package/lib/codegen/languages/ts.js +1 -2
  35. package/lib/codegen/languages/ts.js.map +1 -1
  36. package/lib/codegen/parser.js +85 -3
  37. package/lib/codegen/parser.js.map +1 -1
  38. package/lib/codegen/types.js +6 -3
  39. package/lib/codegen/types.js.map +1 -1
  40. package/lib/decoder/DecodeOperation.d.ts +3 -4
  41. package/lib/decoder/DecodeOperation.js +35 -17
  42. package/lib/decoder/DecodeOperation.js.map +1 -1
  43. package/lib/decoder/Decoder.d.ts +5 -6
  44. package/lib/decoder/Decoder.js +10 -10
  45. package/lib/decoder/Decoder.js.map +1 -1
  46. package/lib/decoder/ReferenceTracker.js +4 -2
  47. package/lib/decoder/ReferenceTracker.js.map +1 -1
  48. package/lib/decoder/strategy/RawChanges.js +1 -2
  49. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  50. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  51. package/lib/decoder/strategy/StateCallbacks.js +74 -64
  52. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  53. package/lib/encoder/ChangeTree.d.ts +28 -20
  54. package/lib/encoder/ChangeTree.js +242 -188
  55. package/lib/encoder/ChangeTree.js.map +1 -1
  56. package/lib/encoder/EncodeOperation.d.ts +3 -6
  57. package/lib/encoder/EncodeOperation.js +51 -65
  58. package/lib/encoder/EncodeOperation.js.map +1 -1
  59. package/lib/encoder/Encoder.d.ts +8 -7
  60. package/lib/encoder/Encoder.js +128 -79
  61. package/lib/encoder/Encoder.js.map +1 -1
  62. package/lib/encoder/Root.d.ts +22 -0
  63. package/lib/encoder/Root.js +81 -0
  64. package/lib/encoder/Root.js.map +1 -0
  65. package/lib/encoder/StateView.d.ts +7 -7
  66. package/lib/encoder/StateView.js +72 -74
  67. package/lib/encoder/StateView.js.map +1 -1
  68. package/lib/encoding/assert.d.ts +7 -6
  69. package/lib/encoding/assert.js +13 -5
  70. package/lib/encoding/assert.js.map +1 -1
  71. package/lib/encoding/decode.d.ts +36 -19
  72. package/lib/encoding/decode.js +54 -84
  73. package/lib/encoding/decode.js.map +1 -1
  74. package/lib/encoding/encode.d.ts +36 -18
  75. package/lib/encoding/encode.js +61 -48
  76. package/lib/encoding/encode.js.map +1 -1
  77. package/lib/encoding/spec.d.ts +4 -5
  78. package/lib/encoding/spec.js +1 -2
  79. package/lib/encoding/spec.js.map +1 -1
  80. package/lib/index.d.ts +10 -9
  81. package/lib/index.js +24 -17
  82. package/lib/index.js.map +1 -1
  83. package/lib/types/HelperTypes.d.ts +34 -2
  84. package/lib/types/HelperTypes.js.map +1 -1
  85. package/lib/types/TypeContext.d.ts +29 -0
  86. package/lib/types/TypeContext.js +151 -0
  87. package/lib/types/TypeContext.js.map +1 -0
  88. package/lib/types/custom/ArraySchema.d.ts +2 -2
  89. package/lib/types/custom/ArraySchema.js +33 -22
  90. package/lib/types/custom/ArraySchema.js.map +1 -1
  91. package/lib/types/custom/CollectionSchema.d.ts +2 -2
  92. package/lib/types/custom/CollectionSchema.js +1 -0
  93. package/lib/types/custom/CollectionSchema.js.map +1 -1
  94. package/lib/types/custom/MapSchema.d.ts +18 -16
  95. package/lib/types/custom/MapSchema.js +12 -4
  96. package/lib/types/custom/MapSchema.js.map +1 -1
  97. package/lib/types/custom/SetSchema.d.ts +2 -2
  98. package/lib/types/custom/SetSchema.js +1 -0
  99. package/lib/types/custom/SetSchema.js.map +1 -1
  100. package/lib/types/registry.d.ts +8 -1
  101. package/lib/types/registry.js +23 -6
  102. package/lib/types/registry.js.map +1 -1
  103. package/lib/types/symbols.d.ts +8 -5
  104. package/lib/types/symbols.js +9 -6
  105. package/lib/types/symbols.js.map +1 -1
  106. package/lib/types/utils.js +1 -2
  107. package/lib/types/utils.js.map +1 -1
  108. package/lib/utils.js +9 -7
  109. package/lib/utils.js.map +1 -1
  110. package/package.json +19 -18
  111. package/src/Metadata.ts +190 -42
  112. package/src/Reflection.ts +76 -38
  113. package/src/Schema.ts +72 -70
  114. package/src/annotations.ts +156 -202
  115. package/src/codegen/languages/csharp.ts +8 -47
  116. package/src/codegen/languages/haxe.ts +4 -0
  117. package/src/codegen/languages/lua.ts +19 -27
  118. package/src/codegen/parser.ts +107 -0
  119. package/src/codegen/types.ts +1 -0
  120. package/src/decoder/DecodeOperation.ts +43 -15
  121. package/src/decoder/Decoder.ts +12 -10
  122. package/src/decoder/ReferenceTracker.ts +5 -3
  123. package/src/decoder/strategy/StateCallbacks.ts +152 -81
  124. package/src/encoder/ChangeTree.ts +282 -209
  125. package/src/encoder/EncodeOperation.ts +78 -78
  126. package/src/encoder/Encoder.ts +152 -88
  127. package/src/encoder/Root.ts +93 -0
  128. package/src/encoder/StateView.ts +80 -88
  129. package/src/encoding/assert.ts +17 -8
  130. package/src/encoding/decode.ts +73 -93
  131. package/src/encoding/encode.ts +76 -45
  132. package/src/encoding/spec.ts +3 -5
  133. package/src/index.ts +12 -20
  134. package/src/types/HelperTypes.ts +54 -2
  135. package/src/types/TypeContext.ts +175 -0
  136. package/src/types/custom/ArraySchema.ts +49 -19
  137. package/src/types/custom/CollectionSchema.ts +1 -0
  138. package/src/types/custom/MapSchema.ts +30 -17
  139. package/src/types/custom/SetSchema.ts +1 -0
  140. package/src/types/registry.ts +22 -3
  141. package/src/types/symbols.ts +10 -7
  142. package/src/utils.ts +7 -3
  143. package/lib/Decoder.d.ts +0 -16
  144. package/lib/Decoder.js +0 -182
  145. package/lib/Decoder.js.map +0 -1
  146. package/lib/Encoder.d.ts +0 -13
  147. package/lib/Encoder.js +0 -79
  148. package/lib/Encoder.js.map +0 -1
  149. package/lib/changes/ChangeSet.d.ts +0 -12
  150. package/lib/changes/ChangeSet.js +0 -35
  151. package/lib/changes/ChangeSet.js.map +0 -1
  152. package/lib/changes/ChangeTree.d.ts +0 -53
  153. package/lib/changes/ChangeTree.js +0 -202
  154. package/lib/changes/ChangeTree.js.map +0 -1
  155. package/lib/changes/DecodeOperation.d.ts +0 -15
  156. package/lib/changes/DecodeOperation.js +0 -186
  157. package/lib/changes/DecodeOperation.js.map +0 -1
  158. package/lib/changes/EncodeOperation.d.ts +0 -18
  159. package/lib/changes/EncodeOperation.js +0 -130
  160. package/lib/changes/EncodeOperation.js.map +0 -1
  161. package/lib/changes/ReferenceTracker.d.ts +0 -14
  162. package/lib/changes/ReferenceTracker.js +0 -83
  163. package/lib/changes/ReferenceTracker.js.map +0 -1
  164. package/lib/changes/consts.d.ts +0 -14
  165. package/lib/changes/consts.js +0 -18
  166. package/lib/changes/consts.js.map +0 -1
  167. package/lib/decoding/decode.d.ts +0 -48
  168. package/lib/decoding/decode.js +0 -267
  169. package/lib/decoding/decode.js.map +0 -1
  170. package/lib/ecs.d.ts +0 -11
  171. package/lib/ecs.js +0 -160
  172. package/lib/ecs.js.map +0 -1
  173. package/lib/filters/index.d.ts +0 -8
  174. package/lib/filters/index.js +0 -24
  175. package/lib/filters/index.js.map +0 -1
  176. package/lib/spec.d.ts +0 -13
  177. package/lib/spec.js +0 -42
  178. package/lib/spec.js.map +0 -1
  179. package/lib/types/ArraySchema.d.ts +0 -238
  180. package/lib/types/ArraySchema.js +0 -555
  181. package/lib/types/ArraySchema.js.map +0 -1
  182. package/lib/types/CollectionSchema.d.ts +0 -35
  183. package/lib/types/CollectionSchema.js +0 -150
  184. package/lib/types/CollectionSchema.js.map +0 -1
  185. package/lib/types/MapSchema.d.ts +0 -38
  186. package/lib/types/MapSchema.js +0 -215
  187. package/lib/types/MapSchema.js.map +0 -1
  188. package/lib/types/SetSchema.d.ts +0 -32
  189. package/lib/types/SetSchema.js +0 -162
  190. package/lib/types/SetSchema.js.map +0 -1
  191. package/lib/types/typeRegistry.d.ts +0 -5
  192. package/lib/types/typeRegistry.js +0 -13
  193. package/lib/types/typeRegistry.js.map +0 -1
  194. package/lib/usage.d.ts +0 -1
  195. package/lib/usage.js +0 -22
  196. package/lib/usage.js.map +0 -1
  197. package/lib/v3.d.ts +0 -1
  198. package/lib/v3.js +0 -427
  199. package/lib/v3.js.map +0 -1
  200. package/lib/v3_experiment.d.ts +0 -1
  201. package/lib/v3_experiment.js +0 -407
  202. package/lib/v3_experiment.js.map +0 -1
package/src/Metadata.ts CHANGED
@@ -1,134 +1,282 @@
1
- import { getPropertyDescriptor, type DefinitionType } from "./annotations";
1
+ import { 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
- { [-1]: number; } & // number of fields
15
- { [-2]: number[]; } & // all field indexes with "view" tag
16
- { [-3]: {[tag: number]: number[]}; } & // field indexes by "view" tag
17
- { [field: number]: string; } & // index => field name
18
- { [field: string]: MetadataField; } // field name => field metadata
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, field: string, type: DefinitionType, descriptor?: PropertyDescriptor) {
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 '${field}'.\nSchema instances may only have up to 64 fields.`);
37
+ throw new Error(`Can't define field '${name}'.\nSchema instances may only have up to 64 fields.`);
25
38
  }
26
39
 
27
- metadata[field] = Object.assign(
28
- metadata[field] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
40
+ metadata[index] = Object.assign(
41
+ metadata[index] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
29
42
  {
30
- type: (Array.isArray(type))
31
- ? { array: type[0] }
32
- : type,
43
+ type: getNormalizedType(type),
33
44
  index,
34
- descriptor,
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, -1, {
76
+ Object.defineProperty(metadata, $numFields, {
40
77
  value: index,
41
78
  enumerable: false,
42
79
  configurable: true
43
80
  });
44
81
 
45
- // map index => field name (non enumerable)
46
- Object.defineProperty(metadata, index, {
47
- value: field,
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[-2]) {
109
+ if (!metadata[$viewFieldIndexes]) {
59
110
  // -2: all field indexes with "view" tag
60
- Object.defineProperty(metadata, -2, {
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, -3, {
118
+ Object.defineProperty(metadata, $fieldIndexesByViewTag, {
68
119
  value: {},
69
120
  enumerable: false,
70
121
  configurable: true
71
122
  });
72
123
  }
73
124
 
74
- metadata[-2].push(field.index);
125
+ metadata[$viewFieldIndexes].push(index);
75
126
 
76
- if (!metadata[-3][tag]) {
77
- metadata[-3][tag] = [];
127
+ if (!metadata[$fieldIndexesByViewTag][tag]) {
128
+ metadata[$fieldIndexesByViewTag][tag] = [];
78
129
  }
79
130
 
80
- metadata[-3][tag].push(field.index);
131
+ metadata[$fieldIndexesByViewTag][tag].push(index);
81
132
  },
82
133
 
83
134
  setFields(target: any, fields: { [field: string]: DefinitionType }) {
84
- const metadata = (target.prototype.constructor[Symbol.metadata] ??= {});
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
- // target[$track] = function (changeTree, index: number, operation: OPERATION = OPERATION.ADD) {
87
- // changeTree.change(index, operation, encodeSchemaOperation);
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
- // target[$encoder] = encodeSchemaOperation;
91
- // target[$decoder] = decodeSchemaOperation;
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
- // if (!target.prototype.toJSON) { target.prototype.toJSON = Schema.prototype.toJSON; }
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
- index,
173
+ fieldIndex,
107
174
  field,
108
175
  type,
109
- getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass, metadata, field)
176
+ getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass)
110
177
  );
111
178
 
112
- index++;
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], -1) as boolean
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[-1]; i++) {
131
- fields[metadata[i]] = metadata[metadata[i]].type;
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,4 +1,5 @@
1
- import { type, PrimitiveType, DefinitionType, TypeContext } from "./annotations";
1
+ import { type, PrimitiveType } 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";
@@ -22,18 +23,32 @@ export class ReflectionType extends Schema {
22
23
  }
23
24
 
24
25
  export class Reflection extends Schema {
25
- @type([ ReflectionType ]) types: ArraySchema<ReflectionType> = new ArraySchema<ReflectionType>();
26
-
27
- static encode(instance: Schema, context?: TypeContext, it: Iterator = { offset: 0 }) {
28
- if (!context) {
29
- context = new TypeContext(instance.constructor as typeof Schema);
30
- }
26
+ @type([ReflectionType]) types: ArraySchema<ReflectionType> = new ArraySchema<ReflectionType>();
27
+ @type("number") rootType: number;
28
+
29
+ /**
30
+ * Encodes the TypeContext of an Encoder into a buffer.
31
+ *
32
+ * @param encoder Encoder instance
33
+ * @param it
34
+ * @returns
35
+ */
36
+ static encode(encoder: Encoder, it: Iterator = { offset: 0 }) {
37
+ const context = encoder.context;
31
38
 
32
39
  const reflection = new Reflection();
33
- const encoder = new Encoder(reflection);
40
+ const reflectionEncoder = new Encoder(reflection);
41
+
42
+ // rootType is usually the first schema passed to the Encoder
43
+ // (unless it inherits from another schema)
44
+ const rootType = context.schemas.get(encoder.state.constructor);
45
+ if (rootType > 0) { reflection.rootType = rootType; }
34
46
 
35
47
  const buildType = (currentType: ReflectionType, metadata: Metadata) => {
36
- for (const fieldName in metadata) {
48
+ for (const fieldIndex in metadata) {
49
+ const index = Number(fieldIndex);
50
+ const fieldName = metadata[index].name;
51
+
37
52
  // skip fields from parent classes
38
53
  if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
39
54
  continue;
@@ -44,7 +59,7 @@ export class Reflection extends Schema {
44
59
 
45
60
  let fieldType: string;
46
61
 
47
- const type = metadata[fieldName].type;
62
+ const type = metadata[index].type;
48
63
 
49
64
  if (typeof (type) === "string") {
50
65
  fieldType = type;
@@ -96,72 +111,95 @@ export class Reflection extends Schema {
96
111
  buildType(type, klass[Symbol.metadata]);
97
112
  }
98
113
 
99
- const buf = encoder.encodeAll(it);
114
+ const buf = reflectionEncoder.encodeAll(it);
100
115
  return Buffer.from(buf, 0, it.offset);
101
116
  }
102
117
 
103
- static decode<T extends Schema = Schema>(bytes: Buffer, it?: Iterator): T {
118
+ /**
119
+ * Decodes the TypeContext from a buffer into a Decoder instance.
120
+ *
121
+ * @param bytes Reflection.encode() output
122
+ * @param it
123
+ * @returns Decoder instance
124
+ */
125
+ static decode<T extends Schema = Schema>(bytes: Buffer, it?: Iterator): Decoder<T> {
104
126
  const reflection = new Reflection();
105
127
 
106
128
  const reflectionDecoder = new Decoder(reflection);
107
129
  reflectionDecoder.decode(bytes, it);
108
130
 
109
- const context = new TypeContext();
110
-
111
- const schemaTypes = reflection.types.reduce((types, reflectionType) => {
112
- const parentKlass: typeof Schema = types[reflectionType.extendsId] || Schema;
113
- const schema: typeof Schema = class _ extends parentKlass {};
131
+ const typeContext = new TypeContext();
114
132
 
115
- // const _metadata = Object.create(_classSuper[Symbol.metadata] ?? null);
116
- const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
117
- Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata })
133
+ // 1st pass, initialize metadata + inheritance
134
+ reflection.types.forEach((reflectionType) => {
135
+ const parentClass: typeof Schema = typeContext.get(reflectionType.extendsId) ?? Schema;
136
+ const schema: typeof Schema = class _ extends parentClass {};
118
137
 
119
138
  // register for inheritance support
120
139
  TypeContext.register(schema);
121
140
 
122
- const typeid = reflectionType.id;
123
- types[typeid] = schema
124
- context.add(schema, typeid);
125
- return types;
126
- }, {});
141
+ // // for inheritance support
142
+ // Metadata.initialize(schema);
127
143
 
128
- reflection.types.forEach((reflectionType) => {
129
- const schemaType = schemaTypes[reflectionType.id];
130
- const metadata = schemaType[Symbol.metadata];
131
-
132
- const parentKlass = reflection.types[reflectionType.extendsId];
133
- const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
144
+ typeContext.add(schema, reflectionType.id);
145
+ }, {});
134
146
 
147
+ // define fields
148
+ const addFields = (metadata: Metadata, reflectionType: ReflectionType, parentFieldIndex: number) => {
135
149
  reflectionType.fields.forEach((field, i) => {
136
150
  const fieldIndex = parentFieldIndex + i;
137
151
 
138
152
  if (field.referencedType !== undefined) {
139
153
  let fieldType = field.type;
140
- let refType = schemaTypes[field.referencedType];
154
+ let refType: PrimitiveType = typeContext.get(field.referencedType);
141
155
 
142
156
  // map or array of primitive type (-1)
143
157
  if (!refType) {
144
158
  const typeInfo = field.type.split(":");
145
159
  fieldType = typeInfo[0];
146
- refType = typeInfo[1];
160
+ refType = typeInfo[1] as PrimitiveType; // string
147
161
  }
148
162
 
149
163
  if (fieldType === "ref") {
150
- // type(refType)(schemaType.prototype, field.name);
151
164
  Metadata.addField(metadata, fieldIndex, field.name, refType);
152
165
 
153
166
  } else {
154
- // type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
155
- Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType } as DefinitionType);
167
+ Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType });
156
168
  }
157
169
 
158
170
  } else {
159
- // type(field.type as PrimitiveType)(schemaType.prototype, field.name);
160
171
  Metadata.addField(metadata, fieldIndex, field.name, field.type as PrimitiveType);
161
172
  }
162
173
  });
174
+ };
175
+
176
+ // 2nd pass, set fields
177
+ reflection.types.forEach((reflectionType) => {
178
+ const schema = typeContext.get(reflectionType.id);
179
+
180
+ // for inheritance support
181
+ const metadata = Metadata.initialize(schema);
182
+
183
+ const inheritedTypes: ReflectionType[] = [];
184
+
185
+ let parentType: ReflectionType = reflectionType;
186
+ do {
187
+ inheritedTypes.push(parentType);
188
+ parentType = reflection.types.find((t) => t.id === parentType.extendsId);
189
+ } while (parentType);
190
+
191
+ let parentFieldIndex = 0;
192
+
193
+ inheritedTypes.reverse().forEach((reflectionType) => {
194
+ // add fields from all inherited classes
195
+ // TODO: refactor this to avoid adding fields from parent classes
196
+ addFields(metadata, reflectionType, parentFieldIndex);
197
+ parentFieldIndex += reflectionType.fields.length;
198
+ });
163
199
  });
164
200
 
165
- return new (schemaTypes[0])();
201
+ const state: T = new (typeContext.get(reflection.rootType || 0) as unknown as any)();
202
+
203
+ return new Decoder<T>(state, typeContext);
166
204
  }
167
205
  }