@colyseus/schema 5.0.2 → 5.0.3

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.
@@ -88,6 +88,13 @@ export declare const Metadata: {
88
88
  */
89
89
  setStreamPriority(metadata: Metadata, fieldName: string, fn: (view: any, element: any) => number): void;
90
90
  getStreamPriority(metadata: Metadata | undefined, index: number): (view: any, element: any) => number;
91
+ /**
92
+ * Install a single field with full encoder wiring: accessor descriptor
93
+ * on the prototype + `metadata[$encoders]` slot for primitives. Shared
94
+ * between `Metadata.setFields` (build path) and
95
+ * `Reflection.makeEncodable` (Reflection upgrade path).
96
+ */
97
+ defineField(target: any, metadata: any, fieldIndex: number, fieldName: string, type: DefinitionType): void;
91
98
  setFields<T extends {
92
99
  new (...args: any[]): InstanceType<T>;
93
100
  } = any>(target: T, fields: { [field in keyof InstanceType<T>]?: DefinitionType; }): T;
@@ -25,6 +25,22 @@ interface ReflectionStatic {
25
25
  * @returns Decoder instance
26
26
  */
27
27
  decode: <T extends Schema = Schema>(bytes: Uint8Array, it?: Iterator) => Decoder<T>;
28
+ /**
29
+ * Upgrade a class produced by `Reflection.decode` so its instances
30
+ * can be used as encode sources (for `InputEncoder` or `Encoder`).
31
+ *
32
+ * `Reflection.decode` reconstructs classes with decoder-only field
33
+ * slots — `inst.x = 7` lands as a direct own property and bypasses
34
+ * the change-tracking + `$values` plumbing that encoders rely on.
35
+ * Calling `makeEncodable(ctor)` installs the same prototype accessor
36
+ * descriptors and `metadata[$encoders]` lookup table that the
37
+ * `schema(...)` / `@type` builders install at class-definition time.
38
+ *
39
+ * Idempotent. Pay-as-you-go: callers that only decode never invoke
40
+ * this and pay nothing extra. Must be called BEFORE any instance of
41
+ * the class is constructed and assigned to.
42
+ */
43
+ makeEncodable: (ctor: typeof Schema) => typeof Schema;
28
44
  }
29
45
  /**
30
46
  * Reflection
package/build/index.cjs CHANGED
@@ -1214,6 +1214,33 @@ const Metadata = {
1214
1214
  getStreamPriority(metadata, index) {
1215
1215
  return metadata?.[$streamPriorities]?.[index];
1216
1216
  },
1217
+ /**
1218
+ * Install a single field with full encoder wiring: accessor descriptor
1219
+ * on the prototype + `metadata[$encoders]` slot for primitives. Shared
1220
+ * between `Metadata.setFields` (build path) and
1221
+ * `Reflection.makeEncodable` (Reflection upgrade path).
1222
+ */
1223
+ defineField(target, metadata, fieldIndex, fieldName, type) {
1224
+ const normalized = getNormalizedType(type);
1225
+ const { complexTypeKlass, childType } = resolveFieldType(normalized);
1226
+ Metadata.addField(metadata, fieldIndex, fieldName, normalized, getPropertyDescriptor(fieldName, fieldIndex, childType, complexTypeKlass));
1227
+ // Install accessor descriptor on the prototype (once per class field).
1228
+ if (metadata[$descriptors][fieldName]) {
1229
+ Object.defineProperty(target.prototype, fieldName, metadata[$descriptors][fieldName]);
1230
+ }
1231
+ // Pre-compute encoder function for primitive types.
1232
+ if (typeof normalized === "string") {
1233
+ if (!metadata[$encoders]) {
1234
+ Object.defineProperty(metadata, $encoders, {
1235
+ value: [],
1236
+ enumerable: false,
1237
+ configurable: true,
1238
+ writable: true,
1239
+ });
1240
+ }
1241
+ metadata[$encoders][fieldIndex] = encode[normalized];
1242
+ }
1243
+ },
1217
1244
  setFields(target, fields) {
1218
1245
  // for inheritance support
1219
1246
  const constructor = target.prototype.constructor;
@@ -1251,17 +1278,7 @@ const Metadata = {
1251
1278
  });
1252
1279
  }
1253
1280
  for (const field in fields) {
1254
- const type = getNormalizedType(fields[field]);
1255
- const { complexTypeKlass, childType } = resolveFieldType(type);
1256
- Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(field, fieldIndex, childType, complexTypeKlass));
1257
- // Install accessor descriptor on the prototype (once per class field).
1258
- if (metadata[$descriptors][field]) {
1259
- Object.defineProperty(target.prototype, field, metadata[$descriptors][field]);
1260
- }
1261
- // Pre-compute encoder function for primitive types.
1262
- if (typeof type === "string") {
1263
- metadata[$encoders][fieldIndex] = encode[type];
1264
- }
1281
+ Metadata.defineField(constructor, metadata, fieldIndex, field, fields[field]);
1265
1282
  fieldIndex++;
1266
1283
  }
1267
1284
  return target;
@@ -8108,6 +8125,31 @@ Reflection.decode = function (bytes, it) {
8108
8125
  const state = new (typeContext.get(reflection.rootType || 0))();
8109
8126
  return new Decoder(state, typeContext);
8110
8127
  };
8128
+ Reflection.makeEncodable = function (ctor) {
8129
+ const metadata = ctor[Symbol.metadata];
8130
+ if (!metadata)
8131
+ return ctor;
8132
+ const numFields = metadata[$numFields];
8133
+ if (numFields === undefined)
8134
+ return ctor;
8135
+ // Walk every field index across the inheritance chain. Repeat calls
8136
+ // are cheap: defineField overwrites the same descriptor and re-stamps
8137
+ // the same `metadata[$encoders]` slot (idempotent).
8138
+ for (let i = 0; i <= numFields; i++) {
8139
+ const field = metadata[i];
8140
+ if (!field)
8141
+ continue;
8142
+ Metadata.defineField(ctor, metadata, i, field.name, field.type);
8143
+ }
8144
+ // Invalidate any cached encode descriptor — `getEncodeDescriptor`
8145
+ // memoizes on the constructor. If something already constructed it
8146
+ // (e.g. a prior `InputEncoder(...)` call that threw), drop the stale
8147
+ // entry so the next read sees the upgraded metadata.
8148
+ if (Object.prototype.hasOwnProperty.call(ctor, $encodeDescriptor)) {
8149
+ delete ctor[$encodeDescriptor];
8150
+ }
8151
+ return ctor;
8152
+ };
8111
8153
 
8112
8154
  /**
8113
8155
  * Legacy callback system