@colyseus/schema 5.0.19 → 5.0.21

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 CHANGED
@@ -2,17 +2,18 @@
2
2
  <img src="logo.png?raw=true" width="50%" />
3
3
  <br>
4
4
  <p>
5
- An incremental binary state serializer with delta encoding for games.<br>
5
+ <b>Automatic state replication for multiplayer games.</b><br>
6
6
  Made for <a href="https://github.com/colyseus/colyseus">Colyseus</a>, yet can be used standalone.
7
7
  </p>
8
8
  </div>
9
9
 
10
10
  # Features
11
11
 
12
- - **Incremental State Synchronization**: Send only the properties that have changed.
12
+ - **Plain Objects, Synced**: Assign a property, `push` to an array, or `set` on a map. Change tracking, encoding and decoding happen for you.
13
+ - **Delta Encoding**: Only the properties that changed are sent, in a compact binary format.
13
14
  - **Trigger Callbacks at Decoding**: [Bring your own](https://docs.colyseus.io/state/callbacks/custom) callback system at decoding, or use the built-in one.
14
15
  - **Instance Reference Tracking**: Share references of the same instance across the state.
15
- - **State Views**: Filter properties that should be sent only to specific clients.
16
+ - **State Views**: Per-client visibility. Decide which properties and instances each client receives.
16
17
  - **Reflection**: Encode/Decode schema definitions.
17
18
  - **Schema Generation**: Generate client-side schema files for strictly typed languages.
18
19
  - **Type Safety**: Strictly typed schema definitions.
@@ -76,7 +76,7 @@ export declare const ReflectionField: import("./annotations.js").SchemaWithExten
76
76
  max: number;
77
77
  bits: number;
78
78
  mode: number;
79
- } & {} & Schema<any> & Schema<unknown>, false, true>;
79
+ } & {} & Schema<any> & Schema<unknown>, true, true>;
80
80
  }, import("./index.js").BuilderInitProps<{
81
81
  name: FieldBuilder<string, false, false>;
82
82
  type: FieldBuilder<string, false, false>;
@@ -91,7 +91,7 @@ export declare const ReflectionField: import("./annotations.js").SchemaWithExten
91
91
  max: number;
92
92
  bits: number;
93
93
  mode: number;
94
- } & {} & Schema<any> & Schema<unknown>, false, true>;
94
+ } & {} & Schema<any> & Schema<unknown>, true, true>;
95
95
  }>, typeof Schema>;
96
96
  export type ReflectionField = SchemaType<typeof ReflectionField>;
97
97
  export declare const ReflectionType: import("./annotations.js").SchemaWithExtendsConstructor<{
@@ -139,8 +139,9 @@ type HasExplicitInit<T> = T extends {
139
139
  * (shorthand for `t.ref(Class)`), or a method (attached to the prototype).
140
140
  */
141
141
  export type FieldsAndMethods = Record<string, FieldBuilder<any, boolean, boolean> | (new (...args: any[]) => Schema) | Function>;
142
+ type SchemaInstance<T, P extends typeof Schema> = InferSchemaInstanceType<T> & InstanceType<P>;
142
143
  export interface SchemaWithExtends<T, P extends typeof Schema> {
143
- extend: <T2 extends FieldsAndMethods = FieldsAndMethods>(fields: T2 & ThisType<InferSchemaInstanceType<T & T2>>, name?: string) => SchemaWithExtendsConstructor<T & T2, ExtractInitProps<T & T2>, P>;
144
+ extend: <T2 extends FieldsAndMethods = FieldsAndMethods>(fields: T2 & ThisType<SchemaInstance<T & T2, P>>, name?: string) => SchemaWithExtendsConstructor<T & T2, ExtractInitProps<T & T2>, P>;
144
145
  }
145
146
  /**
146
147
  * Get the type of the schema defined via `schema('Name', {...})` method.
@@ -156,17 +157,18 @@ export type SchemaType<T extends {
156
157
  '~type': any;
157
158
  }> = T['~type'];
158
159
  export interface SchemaWithExtendsConstructor<T, InitProps, P extends typeof Schema> extends SchemaWithExtends<T, P> {
159
- '~type': InferSchemaInstanceType<T>;
160
+ '~type': SchemaInstance<T, P>;
160
161
  new (...args: [
161
162
  InitProps
162
- ] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : HasExplicitInit<T> extends true ? [InitProps] : IsInitPropsRequired<T> extends true ? ([] | [InitProps]) : [InitProps?]): InferSchemaInstanceType<T> & InstanceType<P>;
163
- prototype: InferSchemaInstanceType<T> & InstanceType<P> & {
164
- initialize(...args: [InitProps] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : [InitProps]): void;
165
- };
163
+ ] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : HasExplicitInit<T> extends true ? [InitProps] : IsInitPropsRequired<T> extends true ? ([] | [InitProps]) : [InitProps?]): SchemaInstance<T, P>;
164
+ prototype: SchemaInstance<T, P>;
166
165
  }
167
166
  /**
168
167
  * Define a Schema class declaratively.
169
168
  *
169
+ * `initialize()` acts as the constructor: a class created with `.extend()`
170
+ * inherits the parent's unless it defines its own.
171
+ *
170
172
  * @example
171
173
  * import { schema, t } from '@colyseus/schema';
172
174
  *
@@ -180,5 +182,5 @@ export interface SchemaWithExtendsConstructor<T, InitProps, P extends typeof Sch
180
182
  * weapon: t.string(),
181
183
  * }, 'Warrior');
182
184
  */
183
- export declare function schema<T extends FieldsAndMethods, P extends typeof Schema = typeof Schema>(fieldsAndMethods: T & ThisType<InferSchemaInstanceType<T>>, name?: string, inherits?: P): SchemaWithExtendsConstructor<T, ExtractInitProps<T>, P>;
185
+ export declare function schema<T extends FieldsAndMethods, P extends typeof Schema = typeof Schema>(fieldsAndMethods: T & ThisType<SchemaInstance<T, P>>, name?: string, inherits?: P): SchemaWithExtendsConstructor<T, ExtractInitProps<T>, P>;
184
186
  export {};
@@ -1,10 +1,9 @@
1
- import { Collection, NonFunctionPropNames } from "../../types/HelperTypes.js";
1
+ import { NonFunctionPropNames, CollectionLike } from "../../types/HelperTypes.js";
2
2
  import type { IRef, Ref } from "../../encoder/ChangeTree.js";
3
3
  import { Decoder } from "../Decoder.js";
4
4
  import { DataChange } from "../DecodeOperation.js";
5
5
  import { OPERATION } from "../../encoding/spec.js";
6
6
  import { Schema } from "../../Schema.js";
7
- import { $refId } from "../../types/symbols.js";
8
7
  import { MapSchema } from "../../types/custom/MapSchema.js";
9
8
  import { ArraySchema } from "../../types/custom/ArraySchema.js";
10
9
  import { type SchemaCallbackProxy } from "./getDecoderStateCallbacks.js";
@@ -12,12 +11,12 @@ type PropertyChangeCallback<K> = (currentValue: K, previousValue: K) => void;
12
11
  type KeyValueCallback<K, V> = (key: K, value: V) => void;
13
12
  type ValueKeyCallback<V, K> = (value: V, key: K) => void;
14
13
  type InstanceChangeCallback = () => void;
15
- type PublicPropNames<T> = Exclude<NonFunctionPropNames<T>, typeof $refId> & string;
16
- type CollectionPropNames<T> = Exclude<{
17
- [K in keyof T]: T[K] extends Collection<any, any> ? K : never;
18
- }[keyof T] & string, typeof $refId>;
19
- type CollectionValueType<T, K extends keyof T> = T[K] extends MapSchema<infer V, any> ? V : T[K] extends ArraySchema<infer V> ? V : T[K] extends Collection<any, infer V, any> ? V : never;
20
- type CollectionKeyType<T, K extends keyof T> = T[K] extends MapSchema<any, infer Key> ? Key : T[K] extends ArraySchema<any> ? number : T[K] extends Collection<infer Key, any, any> ? Key : never;
14
+ type PublicPropNames<T> = NonFunctionPropNames<T> & string;
15
+ type CollectionPropNames<T> = {
16
+ [K in keyof T]: T[K] extends CollectionLike<any, any> ? K : never;
17
+ }[keyof T] & string;
18
+ type CollectionValueType<T, K extends keyof T> = T[K] extends MapSchema<infer V, any> ? V : T[K] extends ArraySchema<infer V> ? V : T[K] extends CollectionLike<any, infer V, any> ? V : never;
19
+ type CollectionKeyType<T, K extends keyof T> = T[K] extends MapSchema<any, infer Key> ? Key : T[K] extends ArraySchema<any> ? number : T[K] extends CollectionLike<infer Key, any, any> ? Key : never;
21
20
  export declare class StateCallbackStrategy<TState extends IRef> {
22
21
  protected decoder: Decoder<TState>;
23
22
  protected uniqueRefIds: Set<number>;
@@ -1,4 +1,4 @@
1
- import { Collection, NonFunctionNonPrimitivePropNames, NonFunctionPropNames } from "../../types/HelperTypes.js";
1
+ import { NonFunctionNonPrimitivePropNames, NonFunctionPropNames, CollectionLike } from "../../types/HelperTypes.js";
2
2
  import { Decoder } from "../Decoder.js";
3
3
  import { Schema } from "../../Schema.js";
4
4
  /**
@@ -10,7 +10,7 @@ import { Schema } from "../../Schema.js";
10
10
  */
11
11
  export type SchemaCallbackProxy<RoomState> = (<T>(instance: T) => CallbackProxy<T>);
12
12
  export type GetCallbackProxy = SchemaCallbackProxy<any>;
13
- export type CallbackProxy<T> = unknown extends T ? SchemaCallback<T> & CollectionCallback<any, any> : T extends Collection<infer K, infer V, infer _> ? CollectionCallback<K, V> : SchemaCallback<T>;
13
+ export type CallbackProxy<T> = unknown extends T ? SchemaCallback<T> & CollectionCallback<any, any> : T extends CollectionLike<infer K, infer V, infer _> ? CollectionCallback<K, V> : SchemaCallback<T>;
14
14
  export type SchemaCallback<T> = {
15
15
  /**
16
16
  * Trigger callback when value of a property changes.
package/build/index.cjs CHANGED
@@ -1411,6 +1411,10 @@ const Metadata = {
1411
1411
  });
1412
1412
  }
1413
1413
  for (const field in fields) {
1414
+ // metadata inherits the parent's, so this also catches a redeclared parent field
1415
+ if (metadata[field] !== undefined) {
1416
+ throw new Error(`@colyseus/schema: Duplicate '${field}' definition on '${constructor.name || "(anonymous)"}'.`);
1417
+ }
1414
1418
  Metadata.defineField(constructor, metadata, fieldIndex, field, fields[field]);
1415
1419
  fieldIndex++;
1416
1420
  }
@@ -7275,6 +7279,9 @@ function makeAutoDefaultFactory(rawType) {
7275
7279
  /**
7276
7280
  * Define a Schema class declaratively.
7277
7281
  *
7282
+ * `initialize()` acts as the constructor: a class created with `.extend()`
7283
+ * inherits the parent's unless it defines its own.
7284
+ *
7278
7285
  * @example
7279
7286
  * import { schema, t } from '@colyseus/schema';
7280
7287
  *
@@ -7435,8 +7442,11 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
7435
7442
  return parentProps;
7436
7443
  };
7437
7444
  const hasInitialize = typeof methods.initialize === "function";
7438
- /** @codegen-ignore */
7439
- const klass = Metadata.setFields(class extends inherits {
7445
+ // Like a constructor: the most-derived initialize() runs once, own or
7446
+ // inherited from a schema() parent. A custom base's initialize() is not
7447
+ // picked up — the constructor type only sees the fields map.
7448
+ const initialize = methods.initialize ?? inherits._initialize;
7449
+ const klass = class extends inherits {
7440
7450
  constructor(...args) {
7441
7451
  const props = args[0];
7442
7452
  if (props === undefined) {
@@ -7451,13 +7461,20 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
7451
7461
  // `initialize()` owns the schema fields, so only parent props flow up.
7452
7462
  super(Object.assign(getDefaultValues(), hasInitialize ? getParentProps(props) : props));
7453
7463
  }
7454
- // Only call initialize() on the exact target class, not parents.
7455
- if (hasInitialize && new.target === klass) {
7456
- methods.initialize.apply(this, args);
7464
+ // Only on the exact target class parents' constructors skip it.
7465
+ if (initialize && new.target === klass) {
7466
+ initialize.apply(this, args);
7457
7467
  }
7458
7468
  }
7459
- }, fields);
7469
+ };
7470
+ // named before setFields so a duplicate-field error can say which class
7471
+ if (name) {
7472
+ Object.defineProperty(klass, "name", { value: name });
7473
+ }
7474
+ /** @codegen-ignore */
7475
+ Metadata.setFields(klass, fields);
7460
7476
  klass._getDefaultValues = getDefaultValues;
7477
+ klass._initialize = initialize;
7461
7478
  Object.assign(klass.prototype, methods);
7462
7479
  for (const fieldName in viewTagFields) {
7463
7480
  view(viewTagFields[fieldName])(klass.prototype, fieldName);
@@ -7489,9 +7506,6 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
7489
7506
  metadata[metadata[fieldName]].optional = true;
7490
7507
  }
7491
7508
  }
7492
- if (name) {
7493
- Object.defineProperty(klass, "name", { value: name });
7494
- }
7495
7509
  klass.extend = (childFields, childName) => schema(childFields, childName, klass);
7496
7510
  return klass;
7497
7511
  }