@colyseus/schema 3.0.0-alpha.3 → 3.0.0-alpha.31

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 (134) hide show
  1. package/README.md +131 -61
  2. package/build/cjs/index.js +966 -563
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +965 -562
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +966 -563
  7. package/lib/Metadata.d.ts +15 -4
  8. package/lib/Metadata.js +86 -18
  9. package/lib/Metadata.js.map +1 -1
  10. package/lib/Reflection.d.ts +2 -3
  11. package/lib/Reflection.js +24 -28
  12. package/lib/Reflection.js.map +1 -1
  13. package/lib/Schema.d.ts +2 -2
  14. package/lib/Schema.js +28 -41
  15. package/lib/Schema.js.map +1 -1
  16. package/lib/annotations.d.ts +1 -21
  17. package/lib/annotations.js +73 -153
  18. package/lib/annotations.js.map +1 -1
  19. package/lib/bench_encode.d.ts +1 -0
  20. package/lib/bench_encode.js +142 -0
  21. package/lib/bench_encode.js.map +1 -0
  22. package/lib/codegen/api.js +1 -2
  23. package/lib/codegen/api.js.map +1 -1
  24. package/lib/codegen/languages/cpp.js +1 -2
  25. package/lib/codegen/languages/cpp.js.map +1 -1
  26. package/lib/codegen/languages/csharp.js +1 -2
  27. package/lib/codegen/languages/csharp.js.map +1 -1
  28. package/lib/codegen/languages/haxe.js +1 -2
  29. package/lib/codegen/languages/haxe.js.map +1 -1
  30. package/lib/codegen/languages/java.js +1 -2
  31. package/lib/codegen/languages/java.js.map +1 -1
  32. package/lib/codegen/languages/js.js +1 -2
  33. package/lib/codegen/languages/js.js.map +1 -1
  34. package/lib/codegen/languages/lua.js +1 -2
  35. package/lib/codegen/languages/lua.js.map +1 -1
  36. package/lib/codegen/languages/ts.js +1 -2
  37. package/lib/codegen/languages/ts.js.map +1 -1
  38. package/lib/codegen/parser.js +2 -3
  39. package/lib/codegen/parser.js.map +1 -1
  40. package/lib/codegen/types.js +3 -3
  41. package/lib/codegen/types.js.map +1 -1
  42. package/lib/debug.d.ts +1 -0
  43. package/lib/debug.js +52 -0
  44. package/lib/debug.js.map +1 -0
  45. package/lib/decoder/DecodeOperation.d.ts +0 -1
  46. package/lib/decoder/DecodeOperation.js +23 -11
  47. package/lib/decoder/DecodeOperation.js.map +1 -1
  48. package/lib/decoder/Decoder.d.ts +6 -7
  49. package/lib/decoder/Decoder.js +8 -8
  50. package/lib/decoder/Decoder.js.map +1 -1
  51. package/lib/decoder/ReferenceTracker.js +3 -2
  52. package/lib/decoder/ReferenceTracker.js.map +1 -1
  53. package/lib/decoder/strategy/RawChanges.js +1 -2
  54. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  55. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  56. package/lib/decoder/strategy/StateCallbacks.js +75 -65
  57. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  58. package/lib/encoder/ChangeTree.d.ts +9 -19
  59. package/lib/encoder/ChangeTree.js +129 -145
  60. package/lib/encoder/ChangeTree.js.map +1 -1
  61. package/lib/encoder/EncodeOperation.d.ts +1 -5
  62. package/lib/encoder/EncodeOperation.js +74 -58
  63. package/lib/encoder/EncodeOperation.js.map +1 -1
  64. package/lib/encoder/Encoder.d.ts +10 -8
  65. package/lib/encoder/Encoder.js +89 -56
  66. package/lib/encoder/Encoder.js.map +1 -1
  67. package/lib/encoder/Root.d.ts +17 -0
  68. package/lib/encoder/Root.js +44 -0
  69. package/lib/encoder/Root.js.map +1 -0
  70. package/lib/encoder/StateView.d.ts +2 -2
  71. package/lib/encoder/StateView.js +49 -59
  72. package/lib/encoder/StateView.js.map +1 -1
  73. package/lib/encoding/assert.d.ts +2 -1
  74. package/lib/encoding/assert.js +5 -5
  75. package/lib/encoding/assert.js.map +1 -1
  76. package/lib/encoding/decode.js +21 -22
  77. package/lib/encoding/decode.js.map +1 -1
  78. package/lib/encoding/encode.d.ts +2 -2
  79. package/lib/encoding/encode.js +40 -39
  80. package/lib/encoding/encode.js.map +1 -1
  81. package/lib/encoding/spec.d.ts +2 -1
  82. package/lib/encoding/spec.js +1 -0
  83. package/lib/encoding/spec.js.map +1 -1
  84. package/lib/index.d.ts +6 -3
  85. package/lib/index.js +18 -13
  86. package/lib/index.js.map +1 -1
  87. package/lib/types/TypeContext.d.ts +23 -0
  88. package/lib/types/TypeContext.js +102 -0
  89. package/lib/types/TypeContext.js.map +1 -0
  90. package/lib/types/custom/ArraySchema.d.ts +2 -2
  91. package/lib/types/custom/ArraySchema.js +6 -9
  92. package/lib/types/custom/ArraySchema.js.map +1 -1
  93. package/lib/types/custom/CollectionSchema.js +1 -0
  94. package/lib/types/custom/CollectionSchema.js.map +1 -1
  95. package/lib/types/custom/MapSchema.js +5 -0
  96. package/lib/types/custom/MapSchema.js.map +1 -1
  97. package/lib/types/custom/SetSchema.js +1 -0
  98. package/lib/types/custom/SetSchema.js.map +1 -1
  99. package/lib/types/registry.js +3 -4
  100. package/lib/types/registry.js.map +1 -1
  101. package/lib/types/symbols.d.ts +1 -0
  102. package/lib/types/symbols.js +2 -1
  103. package/lib/types/symbols.js.map +1 -1
  104. package/lib/types/utils.js +1 -2
  105. package/lib/types/utils.js.map +1 -1
  106. package/lib/utils.js +3 -4
  107. package/lib/utils.js.map +1 -1
  108. package/package.json +5 -5
  109. package/src/Metadata.ts +104 -26
  110. package/src/Reflection.ts +26 -28
  111. package/src/Schema.ts +35 -47
  112. package/src/annotations.ts +82 -176
  113. package/src/bench_encode.ts +121 -0
  114. package/src/debug.ts +56 -0
  115. package/src/decoder/DecodeOperation.ts +28 -11
  116. package/src/decoder/Decoder.ts +13 -11
  117. package/src/decoder/ReferenceTracker.ts +3 -2
  118. package/src/decoder/strategy/StateCallbacks.ts +152 -81
  119. package/src/encoder/ChangeTree.ts +147 -166
  120. package/src/encoder/EncodeOperation.ts +93 -70
  121. package/src/encoder/Encoder.ts +111 -65
  122. package/src/encoder/Root.ts +51 -0
  123. package/src/encoder/StateView.ts +53 -69
  124. package/src/encoding/assert.ts +4 -3
  125. package/src/encoding/decode.ts +1 -2
  126. package/src/encoding/encode.ts +25 -22
  127. package/src/encoding/spec.ts +1 -0
  128. package/src/index.ts +8 -14
  129. package/src/types/TypeContext.ts +122 -0
  130. package/src/types/custom/ArraySchema.ts +10 -2
  131. package/src/types/custom/CollectionSchema.ts +1 -0
  132. package/src/types/custom/MapSchema.ts +6 -0
  133. package/src/types/custom/SetSchema.ts +1 -0
  134. package/src/types/symbols.ts +2 -0
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@colyseus/schema",
3
- "version": "3.0.0-alpha.3",
3
+ "version": "3.0.0-alpha.31",
4
4
  "description": "Binary state serializer with delta encoding for games",
5
5
  "bin": {
6
6
  "schema-codegen": "./bin/schema-codegen"
7
7
  },
8
8
  "scripts": {
9
- "build": "tsc && rollup -c rollup.config.js",
9
+ "build": "tsc && rollup -c rollup.config.mjs",
10
10
  "watch": "tsc -w",
11
11
  "test": "mocha --require ts-node/register test/*.test.ts test/**/*.test.ts",
12
12
  "coverage": "nyc mocha --require ts-node/register --require source-map-support/register --recursive test/**Test.ts",
@@ -73,13 +73,13 @@
73
73
  "nanoid": "^3.1.10",
74
74
  "nyc": "^14.1.1",
75
75
  "rimraf": "^3.0.0",
76
- "rollup": "^2.40.0",
76
+ "rollup": "^4.18.0",
77
77
  "sinon": "^7.2.2",
78
78
  "source-map-support": "^0.5.13",
79
79
  "ts-node": "^10.9.2",
80
80
  "tslib": "^2.1.0",
81
- "tsx": "^4.7.0",
82
- "typescript": "^5.3.3"
81
+ "tsx": "^3.13.0",
82
+ "typescript": "^5.4.5"
83
83
  },
84
84
  "nyc": {
85
85
  "extension": [
package/src/Metadata.ts CHANGED
@@ -1,40 +1,65 @@
1
1
  import { getPropertyDescriptor, type DefinitionType } from "./annotations";
2
2
  import { getType } from "./types/registry";
3
+ import { $descriptors } from "./types/symbols";
3
4
 
4
5
  export type MetadataField = {
5
6
  type: DefinitionType,
7
+ name: string,
6
8
  index: number,
7
9
  tag?: number,
8
10
  unreliable?: boolean,
9
11
  deprecated?: boolean,
10
- descriptor?: PropertyDescriptor,
11
12
  };
12
13
 
13
14
  export type Metadata =
14
15
  { [-1]: number; } & // number of fields
15
16
  { [-2]: number[]; } & // all field indexes with "view" tag
16
17
  { [-3]: {[tag: number]: number[]}; } & // field indexes by "view" tag
17
- { [field: number]: string; } & // index => field name
18
- { [field: string]: MetadataField; } // field name => field metadata
18
+ { [-4]: number[]; } & // all field indexes containing Ref types (Schema, ArraySchema, MapSchema, etc)
19
+ { [field: number]: MetadataField; } & // index => field name
20
+ { [field: string]: number; } & // field name => field metadata
21
+ { [$descriptors]: { [field: string]: PropertyDescriptor } } // property descriptors
19
22
 
20
23
  export const Metadata = {
21
24
 
22
- addField(metadata: any, index: number, field: string, type: DefinitionType, descriptor?: PropertyDescriptor) {
25
+ addField(metadata: any, index: number, name: string, type: DefinitionType, descriptor?: PropertyDescriptor) {
23
26
  if (index > 64) {
24
- throw new Error(`Can't define field '${field}'.\nSchema instances may only have up to 64 fields.`);
27
+ throw new Error(`Can't define field '${name}'.\nSchema instances may only have up to 64 fields.`);
25
28
  }
26
29
 
27
- metadata[field] = Object.assign(
28
- metadata[field] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
30
+ metadata[index] = Object.assign(
31
+ metadata[index] || {}, // avoid overwriting previous field metadata (@owned / @deprecated)
29
32
  {
30
33
  type: (Array.isArray(type))
31
34
  ? { array: type[0] }
32
35
  : type,
33
36
  index,
34
- descriptor,
37
+ name,
35
38
  }
36
39
  );
37
40
 
41
+ // create "descriptors" map
42
+ metadata[$descriptors] ??= {};
43
+
44
+ if (descriptor) {
45
+ // for encoder
46
+ metadata[$descriptors][name] = descriptor;
47
+ metadata[$descriptors][`_${name}`] = {
48
+ value: undefined,
49
+ writable: true,
50
+ enumerable: false,
51
+ configurable: true,
52
+ };
53
+ } else {
54
+ // for decoder
55
+ metadata[$descriptors][name] = {
56
+ value: undefined,
57
+ writable: true,
58
+ enumerable: true,
59
+ configurable: true,
60
+ };
61
+ }
62
+
38
63
  // map -1 as last field index
39
64
  Object.defineProperty(metadata, -1, {
40
65
  value: index,
@@ -42,17 +67,31 @@ export const Metadata = {
42
67
  configurable: true
43
68
  });
44
69
 
45
- // map index => field name (non enumerable)
46
- Object.defineProperty(metadata, index, {
47
- value: field,
70
+ // map field name => index (non enumerable)
71
+ Object.defineProperty(metadata, name, {
72
+ value: index,
48
73
  enumerable: false,
49
74
  configurable: true,
50
75
  });
76
+
77
+ // if child Ref/complex type, add to -4
78
+ if (typeof (metadata[index].type) !== "string") {
79
+ if (metadata[-4] === undefined) {
80
+ Object.defineProperty(metadata, -4, {
81
+ value: [],
82
+ enumerable: false,
83
+ configurable: true,
84
+ });
85
+ }
86
+ metadata[-4].push(index);
87
+ }
51
88
  },
52
89
 
53
90
  setTag(metadata: Metadata, fieldName: string, tag: number) {
91
+ const index = metadata[fieldName];
92
+ const field = metadata[index];
93
+
54
94
  // add 'tag' to the field
55
- const field = metadata[fieldName];
56
95
  field.tag = tag;
57
96
 
58
97
  if (!metadata[-2]) {
@@ -71,27 +110,18 @@ export const Metadata = {
71
110
  });
72
111
  }
73
112
 
74
- metadata[-2].push(field.index);
113
+ metadata[-2].push(index);
75
114
 
76
115
  if (!metadata[-3][tag]) {
77
116
  metadata[-3][tag] = [];
78
117
  }
79
118
 
80
- metadata[-3][tag].push(field.index);
119
+ metadata[-3][tag].push(index);
81
120
  },
82
121
 
83
122
  setFields(target: any, fields: { [field: string]: DefinitionType }) {
84
123
  const metadata = (target.prototype.constructor[Symbol.metadata] ??= {});
85
124
 
86
- // target[$track] = function (changeTree, index: number, operation: OPERATION = OPERATION.ADD) {
87
- // changeTree.change(index, operation, encodeSchemaOperation);
88
- // };
89
-
90
- // target[$encoder] = encodeSchemaOperation;
91
- // target[$decoder] = decodeSchemaOperation;
92
-
93
- // if (!target.prototype.toJSON) { target.prototype.toJSON = Schema.prototype.toJSON; }
94
-
95
125
  let index = 0;
96
126
  for (const field in fields) {
97
127
  const type = fields[field];
@@ -106,7 +136,7 @@ export const Metadata = {
106
136
  index,
107
137
  field,
108
138
  type,
109
- getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass, metadata, field)
139
+ getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass)
110
140
  );
111
141
 
112
142
  index++;
@@ -117,6 +147,54 @@ export const Metadata = {
117
147
  return metadata[field].deprecated === true;
118
148
  },
119
149
 
150
+ init(klass: any) {
151
+ //
152
+ // Used only to initialize an empty Schema (Encoder#constructor)
153
+ // TODO: remove/refactor this...
154
+ //
155
+ const metadata = {};
156
+ klass[Symbol.metadata] = metadata;
157
+ Object.defineProperty(metadata, -1, {
158
+ value: 0,
159
+ enumerable: false,
160
+ configurable: true,
161
+ });
162
+ },
163
+
164
+ initialize(constructor: any, parentMetadata?: Metadata) {
165
+ let metadata: Metadata = constructor[Symbol.metadata] ?? Object.create(null);
166
+
167
+ // make sure inherited classes have their own metadata object.
168
+ if (constructor[Symbol.metadata] === parentMetadata) {
169
+ metadata = Object.create(null);
170
+
171
+ if (parentMetadata) {
172
+ // assign parent metadata to current
173
+ Object.assign(metadata, parentMetadata);
174
+
175
+ for (let i = 0; i <= parentMetadata[-1]; i++) {
176
+ const fieldName = parentMetadata[i].name;
177
+ Object.defineProperty(metadata, fieldName, {
178
+ value: parentMetadata[fieldName],
179
+ enumerable: false,
180
+ configurable: true,
181
+ });
182
+ }
183
+
184
+ Object.defineProperty(metadata, -1, {
185
+ value: parentMetadata[-1],
186
+ enumerable: false,
187
+ configurable: true,
188
+ writable: true,
189
+ });
190
+ }
191
+ }
192
+
193
+ constructor[Symbol.metadata] = metadata;
194
+
195
+ return metadata;
196
+ },
197
+
120
198
  isValidInstance(klass: any) {
121
199
  return (
122
200
  klass.constructor[Symbol.metadata] &&
@@ -125,10 +203,10 @@ export const Metadata = {
125
203
  },
126
204
 
127
205
  getFields(klass: any) {
128
- const metadata = klass[Symbol.metadata];
206
+ const metadata: Metadata = klass[Symbol.metadata];
129
207
  const fields = {};
130
208
  for (let i = 0; i <= metadata[-1]; i++) {
131
- fields[metadata[i]] = metadata[metadata[i]].type;
209
+ fields[metadata[i].name] = metadata[i].type;
132
210
  }
133
211
  return fields;
134
212
  }
package/src/Reflection.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type, PrimitiveType, DefinitionType, TypeContext } from "./annotations";
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";
@@ -24,16 +25,17 @@ export class ReflectionType extends Schema {
24
25
  export class Reflection extends Schema {
25
26
  @type([ ReflectionType ]) types: ArraySchema<ReflectionType> = new ArraySchema<ReflectionType>();
26
27
 
27
- static encode (instance: Schema, context?: TypeContext) {
28
- if (!context) {
29
- context = new TypeContext(instance.constructor as typeof Schema);
30
- }
28
+ static encode(instance: Schema, context?: TypeContext, it: Iterator = { offset: 0 }) {
29
+ context ??= new TypeContext(instance.constructor as typeof Schema);
31
30
 
32
31
  const reflection = new Reflection();
33
32
  const encoder = new Encoder(reflection);
34
33
 
35
34
  const buildType = (currentType: ReflectionType, metadata: Metadata) => {
36
- for (const fieldName in metadata) {
35
+ for (const fieldIndex in metadata) {
36
+ const index = Number(fieldIndex);
37
+ const fieldName = metadata[index].name;
38
+
37
39
  // skip fields from parent classes
38
40
  if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
39
41
  continue;
@@ -44,7 +46,7 @@ export class Reflection extends Schema {
44
46
 
45
47
  let fieldType: string;
46
48
 
47
- const type = metadata[fieldName].type;
49
+ const type = metadata[index].type;
48
50
 
49
51
  if (typeof (type) === "string") {
50
52
  fieldType = type;
@@ -96,7 +98,6 @@ export class Reflection extends Schema {
96
98
  buildType(type, klass[Symbol.metadata]);
97
99
  }
98
100
 
99
- const it = { offset: 0 };
100
101
  const buf = encoder.encodeAll(it);
101
102
  return Buffer.from(buf, 0, it.offset);
102
103
  }
@@ -107,62 +108,59 @@ export class Reflection extends Schema {
107
108
  const reflectionDecoder = new Decoder(reflection);
108
109
  reflectionDecoder.decode(bytes, it);
109
110
 
110
- const context = new TypeContext();
111
+ const typeContext = new TypeContext();
111
112
 
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 {};
113
+ // 1st pass, initialize metadata + inheritance
114
+ reflection.types.forEach((reflectionType) => {
115
+ const parentClass: typeof Schema = typeContext.get(reflectionType.extendsId) ?? Schema;
116
+ const schema: typeof Schema = class _ extends parentClass {};
115
117
 
116
- // const _metadata = Object.create(_classSuper[Symbol.metadata] ?? null);
117
- const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
118
- Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata })
118
+ const parentMetadata = parentClass[Symbol.metadata];
119
119
 
120
120
  // register for inheritance support
121
121
  TypeContext.register(schema);
122
122
 
123
- const typeid = reflectionType.id;
124
- types[typeid] = schema
125
- context.add(schema, typeid);
126
- return types;
123
+ // for inheritance support
124
+ Metadata.initialize(schema, parentMetadata);
125
+
126
+ typeContext.add(schema, reflectionType.id);
127
127
  }, {});
128
128
 
129
+ // 2nd pass, set fields
129
130
  reflection.types.forEach((reflectionType) => {
130
- const schemaType = schemaTypes[reflectionType.id];
131
+ const schemaType = typeContext.get(reflectionType.id);
131
132
  const metadata = schemaType[Symbol.metadata];
132
133
 
133
- const parentKlass = reflection.types[reflectionType.extendsId];
134
- const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
134
+ const parentFieldIndex = 0;
135
135
 
136
136
  reflectionType.fields.forEach((field, i) => {
137
137
  const fieldIndex = parentFieldIndex + i;
138
138
 
139
139
  if (field.referencedType !== undefined) {
140
140
  let fieldType = field.type;
141
- let refType = schemaTypes[field.referencedType];
141
+ let refType: PrimitiveType = typeContext.get(field.referencedType);
142
142
 
143
143
  // map or array of primitive type (-1)
144
144
  if (!refType) {
145
145
  const typeInfo = field.type.split(":");
146
146
  fieldType = typeInfo[0];
147
- refType = typeInfo[1];
147
+ refType = typeInfo[1] as PrimitiveType; // string
148
148
  }
149
149
 
150
150
  if (fieldType === "ref") {
151
- // type(refType)(schemaType.prototype, field.name);
152
151
  Metadata.addField(metadata, fieldIndex, field.name, refType);
153
152
 
154
153
  } else {
155
- // type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
156
154
  Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType } as DefinitionType);
157
155
  }
158
156
 
159
157
  } else {
160
- // type(field.type as PrimitiveType)(schemaType.prototype, field.name);
161
158
  Metadata.addField(metadata, fieldIndex, field.name, field.type as PrimitiveType);
162
159
  }
163
160
  });
164
161
  });
165
162
 
166
- return new (schemaTypes[0])();
163
+ // @ts-ignore
164
+ return new (typeContext.get(0))();
167
165
  }
168
166
  }
package/src/Schema.ts CHANGED
@@ -4,7 +4,7 @@ import { DEFAULT_VIEW_TAG, DefinitionType } from "./annotations";
4
4
  import { NonFunctionPropNames, ToJSON } from './types/HelperTypes';
5
5
 
6
6
  import { ChangeTree, Ref } from './encoder/ChangeTree';
7
- import { $changes, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $track } from './types/symbols';
7
+ import { $changes, $decoder, $deleteByIndex, $descriptors, $encoder, $filter, $getByIndex, $track } from './types/symbols';
8
8
  import { StateView } from './encoder/StateView';
9
9
 
10
10
  import { encodeSchemaOperation } from './encoder/EncodeOperation';
@@ -16,7 +16,6 @@ import { getIndent } from './utils';
16
16
  * Schema encoder / decoder
17
17
  */
18
18
  export abstract class Schema {
19
-
20
19
  static [$encoder] = encodeSchemaOperation;
21
20
  static [$decoder] = decodeSchemaOperation;
22
21
 
@@ -31,37 +30,7 @@ export abstract class Schema {
31
30
  writable: true
32
31
  });
33
32
 
34
- const metadata = instance.constructor[Symbol.metadata];
35
-
36
- // Define property descriptors
37
- for (const field in metadata) {
38
- if (metadata[field].descriptor) {
39
- // for encoder
40
- Object.defineProperty(instance, `_${field}`, {
41
- value: undefined,
42
- writable: true,
43
- enumerable: false,
44
- configurable: true,
45
- });
46
- Object.defineProperty(instance, field, metadata[field].descriptor);
47
-
48
- } else {
49
- // for decoder
50
- Object.defineProperty(instance, field, {
51
- value: undefined,
52
- writable: true,
53
- enumerable: true,
54
- configurable: true,
55
- });
56
- }
57
-
58
- // Object.defineProperty(instance, field, {
59
- // ...instance.constructor[Symbol.metadata][field].descriptor
60
- // });
61
- // if (args[0]?.hasOwnProperty(field)) {
62
- // instance[field] = args[0][field];
63
- // }
64
- }
33
+ Object.defineProperties(instance, instance.constructor[Symbol.metadata]?.[$descriptors] || {});
65
34
  }
66
35
 
67
36
  static is(type: DefinitionType) {
@@ -88,7 +57,7 @@ export abstract class Schema {
88
57
  */
89
58
  static [$filter] (ref: Schema, index: number, view: StateView) {
90
59
  const metadata: Metadata = ref.constructor[Symbol.metadata];
91
- const tag = metadata[metadata[index]].tag;
60
+ const tag = metadata[index]?.tag;
92
61
 
93
62
  if (view === undefined) {
94
63
  // shared pass/encode: encode if doesn't have a tag
@@ -111,13 +80,24 @@ export abstract class Schema {
111
80
 
112
81
  // allow inherited classes to have a constructor
113
82
  constructor(...args: any[]) {
114
- Schema.initialize(this);
83
+ //
84
+ // inline
85
+ // Schema.initialize(this);
86
+ //
87
+
88
+ Object.defineProperty(this, $changes, {
89
+ value: new ChangeTree(this),
90
+ enumerable: false,
91
+ writable: true
92
+ });
93
+
94
+ Object.defineProperties(this, this.constructor[Symbol.metadata]?.[$descriptors] || {});
115
95
 
116
96
  //
117
97
  // Assign initial values
118
98
  //
119
99
  if (args[0]) {
120
- this.assign(args[0]);
100
+ Object.assign(this, args[0]);
121
101
  }
122
102
  }
123
103
 
@@ -135,21 +115,25 @@ export abstract class Schema {
135
115
  * @param operation OPERATION to perform (detected automatically)
136
116
  */
137
117
  public setDirty<K extends NonFunctionPropNames<this>>(property: K | number, operation?: OPERATION) {
118
+ const metadata: Metadata = this.constructor[Symbol.metadata];
138
119
  this[$changes].change(
139
- this.constructor[Symbol.metadata][property as string].index,
120
+ metadata[metadata[property as string]].index,
140
121
  operation
141
122
  );
142
123
  }
143
124
 
144
125
  clone (): this {
145
126
  const cloned = new ((this as any).constructor);
146
- const metadata = this.constructor[Symbol.metadata];
127
+ const metadata: Metadata = this.constructor[Symbol.metadata];
147
128
 
148
129
  //
149
130
  // TODO: clone all properties, not only annotated ones
150
131
  //
151
132
  // for (const field in this) {
152
- for (const field in metadata) {
133
+ for (const fieldIndex in metadata) {
134
+ // const field = metadata[metadata[fieldIndex]].name;
135
+ const field = metadata[fieldIndex as any as number].name;
136
+
153
137
  if (
154
138
  typeof (this[field]) === "object" &&
155
139
  typeof (this[field]?.clone) === "function"
@@ -162,15 +146,17 @@ export abstract class Schema {
162
146
  cloned[field] = this[field];
163
147
  }
164
148
  }
149
+
165
150
  return cloned;
166
151
  }
167
152
 
168
153
  toJSON () {
169
- const metadata = this.constructor[Symbol.metadata];
170
-
171
154
  const obj: unknown = {};
172
- for (const fieldName in metadata) {
173
- const field = metadata[fieldName];
155
+
156
+ const metadata = this.constructor[Symbol.metadata];
157
+ for (const index in metadata) {
158
+ const field = metadata[index];
159
+ const fieldName = field.name;
174
160
  if (!field.deprecated && this[fieldName] !== null && typeof (this[fieldName]) !== "undefined") {
175
161
  obj[fieldName] = (typeof (this[fieldName]['toJSON']) === "function")
176
162
  ? this[fieldName]['toJSON']()
@@ -185,11 +171,13 @@ export abstract class Schema {
185
171
  }
186
172
 
187
173
  protected [$getByIndex](index: number) {
188
- return this[this.constructor[Symbol.metadata][index]];
174
+ const metadata: Metadata = this.constructor[Symbol.metadata];
175
+ return this[metadata[index].name];
189
176
  }
190
177
 
191
178
  protected [$deleteByIndex](index: number) {
192
- this[this.constructor[Symbol.metadata][index]] = undefined;
179
+ const metadata: Metadata = this.constructor[Symbol.metadata];
180
+ this[metadata[index].name] = undefined;
193
181
  }
194
182
 
195
183
  static debugRefIds(instance: Ref, jsonContents: boolean = true, level: number = 0) {
@@ -248,7 +236,7 @@ export abstract class Schema {
248
236
  return output;
249
237
  }
250
238
 
251
- static debugChangesDeep(ref: Ref) {
239
+ static debugChangesDeep(ref: Ref, changeSetName: "changes" | "allChanges" | "allFilteredChanges" | "filteredChanges" = "changes") {
252
240
  let output = "";
253
241
 
254
242
  const rootChangeTree = ref[$changes];
@@ -257,7 +245,7 @@ export abstract class Schema {
257
245
  let totalInstances = 0;
258
246
  let totalOperations = 0;
259
247
 
260
- for (const [changeTree, changes] of (rootChangeTree.root.changes.entries())) {
248
+ for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
261
249
  let includeChangeTree = false;
262
250
  let parentChangeTrees: ChangeTree[] = [];
263
251
  let parentChangeTree = changeTree.parent?.[$changes];