@colyseus/schema 4.0.20 → 5.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.
Files changed (96) hide show
  1. package/README.md +2 -0
  2. package/build/Metadata.d.ts +55 -2
  3. package/build/Reflection.d.ts +24 -30
  4. package/build/Schema.d.ts +70 -9
  5. package/build/annotations.d.ts +56 -13
  6. package/build/codegen/cli.cjs +84 -67
  7. package/build/codegen/cli.cjs.map +1 -1
  8. package/build/decoder/DecodeOperation.d.ts +48 -5
  9. package/build/decoder/Decoder.d.ts +2 -2
  10. package/build/decoder/strategy/Callbacks.d.ts +1 -1
  11. package/build/encoder/ChangeRecorder.d.ts +107 -0
  12. package/build/encoder/ChangeTree.d.ts +218 -69
  13. package/build/encoder/EncodeDescriptor.d.ts +63 -0
  14. package/build/encoder/EncodeOperation.d.ts +25 -2
  15. package/build/encoder/Encoder.d.ts +59 -3
  16. package/build/encoder/MapJournal.d.ts +62 -0
  17. package/build/encoder/RefIdAllocator.d.ts +35 -0
  18. package/build/encoder/Root.d.ts +94 -13
  19. package/build/encoder/StateView.d.ts +116 -8
  20. package/build/encoder/changeTree/inheritedFlags.d.ts +34 -0
  21. package/build/encoder/changeTree/liveIteration.d.ts +3 -0
  22. package/build/encoder/changeTree/parentChain.d.ts +24 -0
  23. package/build/encoder/changeTree/treeAttachment.d.ts +13 -0
  24. package/build/encoder/streaming.d.ts +73 -0
  25. package/build/encoder/subscriptions.d.ts +25 -0
  26. package/build/index.cjs +5202 -1552
  27. package/build/index.cjs.map +1 -1
  28. package/build/index.d.ts +7 -3
  29. package/build/index.js +5202 -1552
  30. package/build/index.mjs +5193 -1552
  31. package/build/index.mjs.map +1 -1
  32. package/build/input/InputDecoder.d.ts +32 -0
  33. package/build/input/InputEncoder.d.ts +117 -0
  34. package/build/input/index.cjs +7429 -0
  35. package/build/input/index.cjs.map +1 -0
  36. package/build/input/index.d.ts +3 -0
  37. package/build/input/index.mjs +7426 -0
  38. package/build/input/index.mjs.map +1 -0
  39. package/build/types/HelperTypes.d.ts +22 -8
  40. package/build/types/TypeContext.d.ts +9 -0
  41. package/build/types/builder.d.ts +162 -0
  42. package/build/types/custom/ArraySchema.d.ts +25 -4
  43. package/build/types/custom/CollectionSchema.d.ts +30 -2
  44. package/build/types/custom/MapSchema.d.ts +52 -3
  45. package/build/types/custom/SetSchema.d.ts +32 -2
  46. package/build/types/custom/StreamSchema.d.ts +114 -0
  47. package/build/types/symbols.d.ts +48 -5
  48. package/package.json +9 -3
  49. package/src/Metadata.ts +258 -31
  50. package/src/Reflection.ts +15 -13
  51. package/src/Schema.ts +176 -134
  52. package/src/annotations.ts +308 -236
  53. package/src/bench_bloat.ts +173 -0
  54. package/src/bench_decode.ts +221 -0
  55. package/src/bench_decode_mem.ts +165 -0
  56. package/src/bench_encode.ts +108 -0
  57. package/src/bench_init.ts +150 -0
  58. package/src/bench_static.ts +109 -0
  59. package/src/bench_stream.ts +295 -0
  60. package/src/bench_view_cmp.ts +142 -0
  61. package/src/codegen/languages/csharp.ts +0 -24
  62. package/src/codegen/parser.ts +83 -61
  63. package/src/decoder/DecodeOperation.ts +168 -63
  64. package/src/decoder/Decoder.ts +20 -10
  65. package/src/decoder/ReferenceTracker.ts +4 -0
  66. package/src/decoder/strategy/Callbacks.ts +30 -26
  67. package/src/decoder/strategy/getDecoderStateCallbacks.ts +16 -13
  68. package/src/encoder/ChangeRecorder.ts +276 -0
  69. package/src/encoder/ChangeTree.ts +674 -519
  70. package/src/encoder/EncodeDescriptor.ts +213 -0
  71. package/src/encoder/EncodeOperation.ts +107 -65
  72. package/src/encoder/Encoder.ts +630 -119
  73. package/src/encoder/MapJournal.ts +124 -0
  74. package/src/encoder/RefIdAllocator.ts +68 -0
  75. package/src/encoder/Root.ts +247 -120
  76. package/src/encoder/StateView.ts +592 -121
  77. package/src/encoder/changeTree/inheritedFlags.ts +217 -0
  78. package/src/encoder/changeTree/liveIteration.ts +74 -0
  79. package/src/encoder/changeTree/parentChain.ts +131 -0
  80. package/src/encoder/changeTree/treeAttachment.ts +171 -0
  81. package/src/encoder/streaming.ts +232 -0
  82. package/src/encoder/subscriptions.ts +71 -0
  83. package/src/index.ts +15 -3
  84. package/src/input/InputDecoder.ts +57 -0
  85. package/src/input/InputEncoder.ts +303 -0
  86. package/src/input/index.ts +3 -0
  87. package/src/types/HelperTypes.ts +21 -9
  88. package/src/types/TypeContext.ts +14 -2
  89. package/src/types/builder.ts +285 -0
  90. package/src/types/custom/ArraySchema.ts +210 -197
  91. package/src/types/custom/CollectionSchema.ts +115 -35
  92. package/src/types/custom/MapSchema.ts +162 -58
  93. package/src/types/custom/SetSchema.ts +128 -39
  94. package/src/types/custom/StreamSchema.ts +310 -0
  95. package/src/types/symbols.ts +54 -6
  96. package/src/utils.ts +4 -6
@@ -1,4 +1,4 @@
1
- export declare const $refId = "~refId";
1
+ export declare const $refId: unique symbol;
2
2
  export declare const $track = "~track";
3
3
  export declare const $encoder = "~encoder";
4
4
  export declare const $decoder = "~decoder";
@@ -6,14 +6,24 @@ export declare const $filter = "~filter";
6
6
  export declare const $getByIndex = "~getByIndex";
7
7
  export declare const $deleteByIndex = "~deleteByIndex";
8
8
  /**
9
- * Used to hold ChangeTree instances whitin the structures
9
+ * Used to hold ChangeTree instances whitin the structures.
10
+ *
11
+ * Real JS Symbol — see the `$values` comment for rationale.
10
12
  */
11
- export declare const $changes = "~changes";
13
+ export declare const $changes: unique symbol;
12
14
  /**
13
15
  * Used to keep track of the type of the child elements of a collection
14
- * (MapSchema, ArraySchema, etc.)
16
+ * (MapSchema, ArraySchema, etc.). Real Symbol — same rationale as $values.
15
17
  */
16
- export declare const $childType = "~childType";
18
+ export declare const $childType: unique symbol;
19
+ /**
20
+ * Self-reference an instance sets on `this` so its own methods can recover
21
+ * the underlying object even when `this` is a Proxy wrapper. Used by
22
+ * ArraySchema (whose public API is a Proxy) to grab the underlying instance
23
+ * once at the top of hot methods and then access fields directly without
24
+ * paying the Proxy.get cost on every read.
25
+ */
26
+ export declare const $proxyTarget: unique symbol;
17
27
  /**
18
28
  * Optional "discard" method for custom types (ArraySchema)
19
29
  * (Discards changes for next serialization)
@@ -23,11 +33,44 @@ export declare const $onEncodeEnd = "~onEncodeEnd";
23
33
  * When decoding, this method is called after the instance is fully decoded
24
34
  */
25
35
  export declare const $onDecodeEnd = "~onDecodeEnd";
36
+ /**
37
+ * Per-instance dense array holding field values by index.
38
+ * Replaces per-field _fieldName shadow properties.
39
+ *
40
+ * Real JS Symbol (not "~"-prefixed string) so plain assignment is safe —
41
+ * symbols are non-enumerable to Object.keys / JSON.stringify / for-in,
42
+ * which means we can drop Object.defineProperty(...{ enumerable: false })
43
+ * and avoid the slow-path / dictionary-mode hazards that come with it.
44
+ */
45
+ export declare const $values: unique symbol;
46
+ /**
47
+ * Brand for FieldBuilder instances so schema() can detect them.
48
+ */
49
+ export declare const $builder = "~builder";
26
50
  /**
27
51
  * Metadata
28
52
  */
29
53
  export declare const $descriptors = "~descriptors";
54
+ /**
55
+ * Per-class bitmask: bit i set iff field i carries a @view tag.
56
+ * Lazily computed from $viewFieldIndexes on first encode pass.
57
+ * Skips the per-field metadata[i].tag property chase in the hot encode loop.
58
+ */
59
+ export declare const $filterBitmask = "~__filterBitmask";
60
+ /**
61
+ * Cached per-class encode descriptor: bundles encoder fn, filter fn,
62
+ * metadata, isSchema flag, and filterBitmask into one object stashed on
63
+ * the constructor. Replaces 5 separate per-tree property chases /
64
+ * function calls in the encode loop with a single property load.
65
+ */
66
+ export declare const $encodeDescriptor = "~__encodeDescriptor";
67
+ export declare const $encoders = "~encoders";
30
68
  export declare const $numFields = "~__numFields";
31
69
  export declare const $refTypeFieldIndexes = "~__refTypeFieldIndexes";
32
70
  export declare const $viewFieldIndexes = "~__viewFieldIndexes";
33
71
  export declare const $fieldIndexesByViewTag = "$__fieldIndexesByViewTag";
72
+ export declare const $unreliableFieldIndexes = "~__unreliableFieldIndexes";
73
+ export declare const $transientFieldIndexes = "~__transientFieldIndexes";
74
+ export declare const $staticFieldIndexes = "~__staticFieldIndexes";
75
+ export declare const $streamFieldIndexes = "~__streamFieldIndexes";
76
+ export declare const $streamPriorities = "~__streamPriorities";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/schema",
3
- "version": "4.0.20",
3
+ "version": "5.0.0",
4
4
  "description": "Binary state serializer with delta encoding for games",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,8 +10,8 @@
10
10
  "scripts": {
11
11
  "build": "tsc -p tsconfig.build.json && rollup -c rollup.config.mjs",
12
12
  "watch": "tsc -p tsconfig.build.json -w",
13
+ "typecheck": "tsc -p tsconfig.build.json --noEmit",
13
14
  "test": "tsx --tsconfig tsconfig.test.json ./node_modules/.bin/mocha test/*.test.ts test/**/*.test.ts",
14
- "typecheck": "tsc -p tsconfig.test.json",
15
15
  "coverage": "c8 npm run test",
16
16
  "generate-test-1": "bin/schema-codegen test-external/PrimitiveTypes.ts --namespace SchemaTest.PrimitiveTypes --output ../colyseus-unity-sdk/Assets/Colyseus/Tests/Editor/ColyseusTests/Schema/PrimitiveTypes",
17
17
  "generate-test-2": "bin/schema-codegen test-external/ChildSchemaTypes.ts --namespace SchemaTest.ChildSchemaTypes --output ../colyseus-unity-sdk/Assets/Colyseus/Tests/Editor/ColyseusTests/Schema/ChildSchemaTypes",
@@ -42,6 +42,12 @@
42
42
  "import": "./build/index.mjs",
43
43
  "require": "./build/index.cjs",
44
44
  "types": "./build/index.d.ts"
45
+ },
46
+ "./input": {
47
+ "browser": "./build/input/index.mjs",
48
+ "import": "./build/input/index.mjs",
49
+ "require": "./build/input/index.cjs",
50
+ "types": "./build/input/index.d.ts"
45
51
  }
46
52
  },
47
53
  "repository": {
@@ -82,7 +88,7 @@
82
88
  "typescript": "^5.9.3"
83
89
  },
84
90
  "peerDependencies": {
85
- "typescript": "^5.0.0 || ^6.0.0"
91
+ "typescript": "^5.9.3"
86
92
  },
87
93
  "c8": {
88
94
  "include": [
package/src/Metadata.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { DefinitionType, getPropertyDescriptor } from "./annotations.js";
2
2
  import { Schema } from "./Schema.js";
3
- import { getType, registeredTypes } from "./types/registry.js";
4
- import { $decoder, $descriptors, $encoder, $fieldIndexesByViewTag, $numFields, $refTypeFieldIndexes, $track, $viewFieldIndexes } from "./types/symbols.js";
3
+ import { getType, registeredTypes, TypeDefinition } from "./types/registry.js";
4
+ import { $decoder, $descriptors, $encoder, $encoders, $fieldIndexesByViewTag, $numFields, $refTypeFieldIndexes, $staticFieldIndexes, $streamFieldIndexes, $streamPriorities, $track, $transientFieldIndexes, $unreliableFieldIndexes, $viewFieldIndexes } from "./types/symbols.js";
5
+ import { ARRAY_STREAM_NOT_SUPPORTED } from "./encoder/streaming.js";
6
+ import { encode } from "./encoding/encode.js";
5
7
  import { TypeContext } from "./types/TypeContext.js";
6
8
 
7
9
  export type MetadataField = {
@@ -10,7 +12,11 @@ export type MetadataField = {
10
12
  index: number,
11
13
  tag?: number,
12
14
  unreliable?: boolean,
15
+ transient?: boolean,
13
16
  deprecated?: boolean,
17
+ owned?: boolean,
18
+ static?: boolean,
19
+ stream?: boolean,
14
20
  };
15
21
 
16
22
  export type Metadata =
@@ -18,10 +24,31 @@ export type Metadata =
18
24
  { [$viewFieldIndexes]: number[]; } & // all field indexes with "view" tag
19
25
  { [$fieldIndexesByViewTag]: {[tag: number]: number[]}; } & // field indexes by "view" tag
20
26
  { [$refTypeFieldIndexes]: number[]; } & // all field indexes containing Ref types (Schema, ArraySchema, MapSchema, etc)
27
+ { [$unreliableFieldIndexes]: number[]; } & // all field indexes tagged with @unreliable
28
+ { [$transientFieldIndexes]: number[]; } & // all field indexes tagged with @transient (not persisted to snapshots)
29
+ { [$staticFieldIndexes]: number[]; } & // all field indexes tagged with @static (not tracked after assignment)
30
+ { [$streamFieldIndexes]: number[]; } & // all field indexes holding a t.stream(...) collection
31
+ { [$streamPriorities]: { [field: number]: (view: any, element: any) => number }; } & // per-stream-field priority callback declared at schema definition time
32
+ { [$encoders]: Array<(bytes: Uint8Array, value: any, it: any) => void>; } & // pre-computed encoder fn per primitive field
21
33
  { [field: number]: MetadataField; } & // index => field name
22
34
  { [field: string]: number; } & // field name => field metadata
23
35
  { [$descriptors]: { [field: string]: PropertyDescriptor } } // property descriptors
24
36
 
37
+ /**
38
+ * Given a normalized field type (`"number"`, `{ map: Foo }`, `Player`,
39
+ * etc.), split into the collection-type descriptor (`{ constructor:
40
+ * MapSchema, ... }`) if applicable and the inner child type. Shared by
41
+ * `@type()` decoration and `Metadata.setFields` — both need to build a
42
+ * property accessor that knows whether the slot holds a collection.
43
+ */
44
+ export function resolveFieldType(type: any): { complexTypeKlass: TypeDefinition | false, childType: any } {
45
+ const complexTypeKlass = typeof (Object.keys(type)[0]) === "string" && getType(Object.keys(type)[0]);
46
+ return {
47
+ complexTypeKlass,
48
+ childType: complexTypeKlass ? Object.values(type)[0] : type,
49
+ };
50
+ }
51
+
25
52
  export function getNormalizedType(type: any): DefinitionType {
26
53
  if (Array.isArray(type)) {
27
54
  return { array: getNormalizedType(type[0]) };
@@ -91,16 +118,11 @@ export const Metadata = {
91
118
  });
92
119
 
93
120
  if (descriptor) {
94
- // for encoder
121
+ // Accessor descriptor for the public field name.
122
+ // Installed on the prototype at class-definition time.
95
123
  metadata[$descriptors][name] = descriptor;
96
- metadata[$descriptors][`_${name}`] = {
97
- value: undefined,
98
- writable: true,
99
- enumerable: false,
100
- configurable: true,
101
- };
102
124
  } else {
103
- // for decoder
125
+ // For decoder: simple writable slot, also on prototype.
104
126
  metadata[$descriptors][name] = {
105
127
  value: undefined,
106
128
  writable: true,
@@ -134,6 +156,38 @@ export const Metadata = {
134
156
  }
135
157
  metadata[$refTypeFieldIndexes].push(index);
136
158
  }
159
+
160
+ // `{ stream: ... }` collections are always view-scoped (priority-
161
+ // batched emit). Auto-flag here so both `@type({stream: ...})` and
162
+ // the `t.stream(...)` builder route into the same filter / encoder
163
+ // dispatch without the caller needing an extra setStream() call.
164
+ const t = metadata[index].type;
165
+ if (t && typeof t === "object" && (t as any)["stream"] !== undefined) {
166
+ // Reject the combined shorthand `@type({ array: X, stream:
167
+ // true })` at decoration time — same diagnostic as the
168
+ // builder chainable throws for `t.array(X).stream()`.
169
+ if ((t as any).array !== undefined) {
170
+ throw new Error(ARRAY_STREAM_NOT_SUPPORTED);
171
+ }
172
+ metadata[index].stream = true;
173
+ if (!metadata[$streamFieldIndexes]) {
174
+ Object.defineProperty(metadata, $streamFieldIndexes, {
175
+ value: [],
176
+ enumerable: false,
177
+ configurable: true,
178
+ writable: true,
179
+ });
180
+ }
181
+ if (!metadata[$streamFieldIndexes].includes(index)) {
182
+ metadata[$streamFieldIndexes].push(index);
183
+ }
184
+ // Pick up the declaration-scope priority callback if present in
185
+ // the `@type({ stream: X, priority: fn })` shorthand.
186
+ const priorityFn = (type as any)?.priority;
187
+ if (typeof priorityFn === "function") {
188
+ Metadata.setStreamPriority(metadata as any, name, priorityFn);
189
+ }
190
+ }
137
191
  },
138
192
 
139
193
  setTag(metadata: Metadata, fieldName: string, tag: number) {
@@ -168,6 +222,112 @@ export const Metadata = {
168
222
  metadata[$fieldIndexesByViewTag][tag].push(index);
169
223
  },
170
224
 
225
+ setUnreliable(metadata: Metadata, fieldName: string) {
226
+ const index = metadata[fieldName];
227
+ const fieldType = metadata[index].type;
228
+ // `@unreliable` is only valid on primitive fields. Ref-type fields
229
+ // (Schema sub-classes, MapSchema, ArraySchema, SetSchema,
230
+ // CollectionSchema) carry refIds whose ADD/DELETE must arrive
231
+ // on the reliable channel — otherwise a dropped unreliable packet
232
+ // would leave the decoder unable to interpret subsequent packets
233
+ // referencing the orphan refId. Primitive types are encoded as
234
+ // strings ("number", "string", "int32", ...); anything else is a
235
+ // ref. Reject at decoration time so the bug surfaces in dev, not
236
+ // under packet loss in prod.
237
+ if (typeof fieldType !== "string") {
238
+ throw new Error(
239
+ `@unreliable cannot be applied to ref-type field "${fieldName}". ` +
240
+ `For ref-type fields, mark each primitive sub-field with @unreliable instead. ` +
241
+ `See README "Limitations and best practices".`
242
+ );
243
+ }
244
+ metadata[index].unreliable = true;
245
+
246
+ if (!metadata[$unreliableFieldIndexes]) {
247
+ Object.defineProperty(metadata, $unreliableFieldIndexes, {
248
+ value: [],
249
+ enumerable: false,
250
+ configurable: true,
251
+ writable: true,
252
+ });
253
+ }
254
+ metadata[$unreliableFieldIndexes].push(index);
255
+ },
256
+
257
+ setTransient(metadata: Metadata, fieldName: string) {
258
+ const index = metadata[fieldName];
259
+ metadata[index].transient = true;
260
+
261
+ if (!metadata[$transientFieldIndexes]) {
262
+ Object.defineProperty(metadata, $transientFieldIndexes, {
263
+ value: [],
264
+ enumerable: false,
265
+ configurable: true,
266
+ writable: true,
267
+ });
268
+ }
269
+ metadata[$transientFieldIndexes].push(index);
270
+ },
271
+
272
+ setStatic(metadata: Metadata, fieldName: string) {
273
+ const index = metadata[fieldName];
274
+ metadata[index].static = true;
275
+
276
+ if (!metadata[$staticFieldIndexes]) {
277
+ Object.defineProperty(metadata, $staticFieldIndexes, {
278
+ value: [],
279
+ enumerable: false,
280
+ configurable: true,
281
+ writable: true,
282
+ });
283
+ }
284
+ metadata[$staticFieldIndexes].push(index);
285
+ },
286
+
287
+ setStream(metadata: Metadata, fieldName: string) {
288
+ const index = metadata[fieldName];
289
+ metadata[index].stream = true;
290
+
291
+ if (!metadata[$streamFieldIndexes]) {
292
+ Object.defineProperty(metadata, $streamFieldIndexes, {
293
+ value: [],
294
+ enumerable: false,
295
+ configurable: true,
296
+ writable: true,
297
+ });
298
+ }
299
+ metadata[$streamFieldIndexes].push(index);
300
+ },
301
+
302
+ /**
303
+ * Attach a declaration-scope priority callback to a stream field.
304
+ * Called at schema definition time (via `t.stream(X).priority(fn)` or
305
+ * `@type({ stream: X, priority: fn })`), looked up at stream-attach
306
+ * time to seed the instance's `_stream.priority` slot. The callback
307
+ * signature is `(view: StateView, element: V) => number` — only fires
308
+ * during `encodeView`, broadcast mode emits FIFO regardless.
309
+ */
310
+ setStreamPriority(
311
+ metadata: Metadata,
312
+ fieldName: string,
313
+ fn: (view: any, element: any) => number,
314
+ ) {
315
+ const index = metadata[fieldName];
316
+ if (!metadata[$streamPriorities]) {
317
+ Object.defineProperty(metadata, $streamPriorities, {
318
+ value: {},
319
+ enumerable: false,
320
+ configurable: true,
321
+ writable: true,
322
+ });
323
+ }
324
+ metadata[$streamPriorities][index] = fn;
325
+ },
326
+
327
+ getStreamPriority(metadata: Metadata | undefined, index: number) {
328
+ return metadata?.[$streamPriorities]?.[index];
329
+ },
330
+
171
331
  setFields<T extends { new (...args: any[]): InstanceType<T> } = any>(target: T, fields: { [field in keyof InstanceType<T>]?: DefinitionType }) {
172
332
  // for inheritance support
173
333
  const constructor = target.prototype.constructor;
@@ -192,24 +352,39 @@ export const Metadata = {
192
352
 
193
353
  fieldIndex++;
194
354
 
355
+ // Pre-computed encoder function table: metadata[$encoders][fieldIndex] = encode.uint8 etc.
356
+ if (!metadata[$encoders]) {
357
+ Object.defineProperty(metadata, $encoders, {
358
+ value: parentMetadata?.[$encoders] ? [...parentMetadata[$encoders]] : [],
359
+ enumerable: false,
360
+ configurable: true,
361
+ writable: true,
362
+ });
363
+ }
364
+
195
365
  for (const field in fields) {
196
366
  const type = getNormalizedType(fields[field]);
197
367
 
198
- // FIXME: this code is duplicated from @type() annotation
199
- const complexTypeKlass = typeof(Object.keys(type)[0]) === "string" && getType(Object.keys(type)[0]);
200
-
201
- const childType = (complexTypeKlass)
202
- ? Object.values(type)[0]
203
- : type;
368
+ const { complexTypeKlass, childType } = resolveFieldType(type);
204
369
 
205
370
  Metadata.addField(
206
371
  metadata,
207
372
  fieldIndex,
208
373
  field,
209
374
  type,
210
- getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass)
375
+ getPropertyDescriptor(field, fieldIndex, childType, complexTypeKlass)
211
376
  );
212
377
 
378
+ // Install accessor descriptor on the prototype (once per class field).
379
+ if (metadata[$descriptors][field]) {
380
+ Object.defineProperty(target.prototype, field, metadata[$descriptors][field]);
381
+ }
382
+
383
+ // Pre-compute encoder function for primitive types.
384
+ if (typeof type === "string") {
385
+ metadata[$encoders][fieldIndex] = (encode as any)[type];
386
+ }
387
+
213
388
  fieldIndex++;
214
389
  }
215
390
 
@@ -220,20 +395,6 @@ export const Metadata = {
220
395
  return metadata[field].deprecated === true;
221
396
  },
222
397
 
223
- init(klass: any) {
224
- //
225
- // Used only to initialize an empty Schema (Encoder#constructor)
226
- // TODO: remove/refactor this...
227
- //
228
- const metadata = {};
229
- klass[Symbol.metadata] = metadata;
230
- Object.defineProperty(metadata, $numFields, {
231
- value: 0,
232
- enumerable: false,
233
- configurable: true,
234
- });
235
- },
236
-
237
398
  initialize(constructor: any) {
238
399
  const parentClass = Object.getPrototypeOf(constructor);
239
400
  const parentMetadata: Metadata = parentClass[Symbol.metadata];
@@ -284,6 +445,46 @@ export const Metadata = {
284
445
  });
285
446
  }
286
447
 
448
+ // $unreliableFieldIndexes
449
+ if (parentMetadata[$unreliableFieldIndexes] !== undefined) {
450
+ Object.defineProperty(metadata, $unreliableFieldIndexes, {
451
+ value: [...parentMetadata[$unreliableFieldIndexes]],
452
+ enumerable: false,
453
+ configurable: true,
454
+ writable: true,
455
+ });
456
+ }
457
+
458
+ // $transientFieldIndexes
459
+ if (parentMetadata[$transientFieldIndexes] !== undefined) {
460
+ Object.defineProperty(metadata, $transientFieldIndexes, {
461
+ value: [...parentMetadata[$transientFieldIndexes]],
462
+ enumerable: false,
463
+ configurable: true,
464
+ writable: true,
465
+ });
466
+ }
467
+
468
+ // $staticFieldIndexes
469
+ if (parentMetadata[$staticFieldIndexes] !== undefined) {
470
+ Object.defineProperty(metadata, $staticFieldIndexes, {
471
+ value: [...parentMetadata[$staticFieldIndexes]],
472
+ enumerable: false,
473
+ configurable: true,
474
+ writable: true,
475
+ });
476
+ }
477
+
478
+ // $streamFieldIndexes
479
+ if (parentMetadata[$streamFieldIndexes] !== undefined) {
480
+ Object.defineProperty(metadata, $streamFieldIndexes, {
481
+ value: [...parentMetadata[$streamFieldIndexes]],
482
+ enumerable: false,
483
+ configurable: true,
484
+ writable: true,
485
+ });
486
+ }
487
+
287
488
  // $descriptors
288
489
  Object.defineProperty(metadata, $descriptors, {
289
490
  value: { ...parentMetadata[$descriptors] },
@@ -291,6 +492,16 @@ export const Metadata = {
291
492
  configurable: true,
292
493
  writable: true,
293
494
  });
495
+
496
+ // $encoders
497
+ if (parentMetadata[$encoders] !== undefined) {
498
+ Object.defineProperty(metadata, $encoders, {
499
+ value: [...parentMetadata[$encoders]],
500
+ enumerable: false,
501
+ configurable: true,
502
+ writable: true,
503
+ });
504
+ }
294
505
  }
295
506
  }
296
507
 
@@ -321,5 +532,21 @@ export const Metadata = {
321
532
 
322
533
  hasViewTagAtIndex(metadata: Metadata, index: number) {
323
534
  return metadata?.[$viewFieldIndexes]?.includes(index);
535
+ },
536
+
537
+ hasUnreliableAtIndex(metadata: Metadata, index: number) {
538
+ return metadata?.[$unreliableFieldIndexes]?.includes(index);
539
+ },
540
+
541
+ hasTransientAtIndex(metadata: Metadata, index: number) {
542
+ return metadata?.[$transientFieldIndexes]?.includes(index);
543
+ },
544
+
545
+ hasStaticAtIndex(metadata: Metadata, index: number) {
546
+ return metadata?.[$staticFieldIndexes]?.includes(index);
547
+ },
548
+
549
+ hasStreamAtIndex(metadata: Metadata, index: number) {
550
+ return metadata?.[$streamFieldIndexes]?.includes(index);
324
551
  }
325
552
  }
package/src/Reflection.ts CHANGED
@@ -5,6 +5,8 @@ import { Iterator } from "./encoding/decode.js";
5
5
  import { Encoder } from "./encoder/Encoder.js";
6
6
  import { Decoder } from "./decoder/Decoder.js";
7
7
  import { Schema } from "./Schema.js";
8
+ import { t, FieldBuilder } from "./types/builder.js";
9
+ import { ArraySchema } from "./types/custom/ArraySchema.js";
8
10
 
9
11
  /**
10
12
  * Static methods available on Reflection
@@ -33,25 +35,25 @@ interface ReflectionStatic {
33
35
  * Reflection
34
36
  */
35
37
  export const ReflectionField = schema({
36
- name: "string",
37
- type: "string",
38
- referencedType: "number",
39
- })
38
+ name: t.string(),
39
+ type: t.string(),
40
+ referencedType: t.number(),
41
+ }, "ReflectionField");
40
42
  export type ReflectionField = SchemaType<typeof ReflectionField>;
41
43
 
42
44
  export const ReflectionType = schema({
43
- id: "number",
44
- extendsId: "number",
45
- fields: [ ReflectionField ],
46
- })
45
+ id: t.number(),
46
+ extendsId: t.number(),
47
+ fields: t.array(ReflectionField),
48
+ }, "ReflectionType");
47
49
  export type ReflectionType = SchemaType<typeof ReflectionType>;
48
50
 
49
51
  export const Reflection = schema({
50
- types: [ ReflectionType ],
51
- rootType: "number",
52
- }) as ReturnType<typeof schema<{
53
- types: [typeof ReflectionType];
54
- rootType: "number";
52
+ types: t.array(ReflectionType),
53
+ rootType: t.number(),
54
+ }, "Reflection") as ReturnType<typeof schema<{
55
+ types: FieldBuilder<ArraySchema<ReflectionType>>;
56
+ rootType: FieldBuilder<number>;
55
57
  }>> & ReflectionStatic;
56
58
 
57
59
  export type Reflection = SchemaType<typeof Reflection>;