@colyseus/schema 5.0.20 → 5.0.22
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/build/Reflection.d.ts +2 -2
- package/build/annotations.d.ts +4 -3
- package/build/index.cjs +23 -9
- package/build/index.cjs.map +1 -1
- package/build/index.js +23 -9
- package/build/index.mjs +23 -9
- package/build/index.mjs.map +1 -1
- package/build/types/HelperTypes.d.ts +14 -2
- package/build/types/builder.d.ts +5 -1
- package/package.json +1 -1
- package/src/Metadata.ts +4 -0
- package/src/annotations.ts +25 -17
- package/src/types/HelperTypes.ts +16 -0
- package/src/types/builder.ts +8 -1
|
@@ -8,7 +8,7 @@ import type { SetSchema } from "./custom/SetSchema.js";
|
|
|
8
8
|
import type { StreamSchema } from "./custom/StreamSchema.js";
|
|
9
9
|
import type { FieldBuilder } from "./builder.js";
|
|
10
10
|
export type Constructor<T = {}> = new (...args: any[]) => T;
|
|
11
|
-
type PrimitiveStringToType<T> = T extends "string" ? string : T extends "number" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" ? number : T extends "boolean" ? boolean : T;
|
|
11
|
+
type PrimitiveStringToType<T> = T extends "string" ? string : T extends "number" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" ? number : T extends "bigint64" | "biguint64" ? bigint : T extends "boolean" ? boolean : T;
|
|
12
12
|
/**
|
|
13
13
|
* What the decoder callbacks accept as "a collection": the public shape, which
|
|
14
14
|
* a plain array satisfies too — `@type([X]) items: X[]` is a common way to
|
|
@@ -23,7 +23,7 @@ export interface Collection<K = any, V = any, IT = V> extends CollectionLike<K,
|
|
|
23
23
|
/** See {@link $resyncPrune} — every collection kind must declare its resync-sweep semantics. */
|
|
24
24
|
[$resyncPrune](visited: Set<number | string>, prune: (value: V, identity: number | string) => void, keep: (value: V) => void): void;
|
|
25
25
|
}
|
|
26
|
-
export type InferValueType<T> = T extends FieldBuilder<infer V> ? V : T extends "string" ? string : T extends "number" ? number : T extends "int8" ? number : T extends "uint8" ? number : T extends "int16" ? number : T extends "uint16" ? number : T extends "int32" ? number : T extends "uint32" ? number : T extends "int64" ? number : T extends "uint64" ? number : T extends "float32" ? number : T extends "float64" ? number : T extends "boolean" ? boolean : T extends {
|
|
26
|
+
export type InferValueType<T> = T extends FieldBuilder<infer V> ? V : T extends "string" ? string : T extends "number" ? number : T extends "int8" ? number : T extends "uint8" ? number : T extends "int16" ? number : T extends "uint16" ? number : T extends "int32" ? number : T extends "uint32" ? number : T extends "int64" ? number : T extends "uint64" ? number : T extends "float32" ? number : T extends "float64" ? number : T extends "bigint64" ? bigint : T extends "biguint64" ? bigint : T extends "boolean" ? boolean : T extends {
|
|
27
27
|
type: infer ChildType extends PrimitiveType;
|
|
28
28
|
} ? InferValueType<ChildType> : T extends {
|
|
29
29
|
type: infer ChildType extends Constructor;
|
|
@@ -76,6 +76,18 @@ export type InferValueType<T> = T extends FieldBuilder<infer V> ? V : T extends
|
|
|
76
76
|
} ? StreamSchema<InstanceType<ChildType>> : T extends {
|
|
77
77
|
stream: infer ChildType;
|
|
78
78
|
} ? StreamSchema<ChildType> : T extends Constructor ? InstanceType<T> : T extends Record<string | number, string | number> ? T[keyof T] : T extends PrimitiveType ? T : never;
|
|
79
|
+
/**
|
|
80
|
+
* Codecs that can carry a `T` — {@link InferValueType} run backwards, derived
|
|
81
|
+
* from it so the two can't drift. Constrains the element refinement in
|
|
82
|
+
* `t.array<Mark>("uint8")`; `never` (an uncallable overload) when no codec
|
|
83
|
+
* decodes into `T`.
|
|
84
|
+
*
|
|
85
|
+
* `[T] extends [...]` is deliberate: a distributive check would let a mixed
|
|
86
|
+
* union like `string | number` match on either half.
|
|
87
|
+
*/
|
|
88
|
+
export type CodecFor<T> = {
|
|
89
|
+
[K in RawPrimitiveType]: [T] extends [InferValueType<K>] ? K : never;
|
|
90
|
+
}[RawPrimitiveType];
|
|
79
91
|
type IsOptionalBuilderKey<T, K extends keyof T> = T[K] extends FieldBuilder<unknown, boolean, infer O extends boolean> ? O : false;
|
|
80
92
|
type OptionalBuilderKeys<T> = {
|
|
81
93
|
[K in keyof T]-?: IsOptionalBuilderKey<T, K> extends true ? K : never;
|
package/build/types/builder.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { CollectionSchema } from "./custom/CollectionSchema.js";
|
|
|
5
5
|
import type { StreamSchema } from "./custom/StreamSchema.js";
|
|
6
6
|
import type { Schema } from "../Schema.js";
|
|
7
7
|
import type { DefinitionType, RawPrimitiveType } from "../annotations.js";
|
|
8
|
-
import type { InferValueType, Constructor } from "./HelperTypes.js";
|
|
8
|
+
import type { InferValueType, Constructor, CodecFor } from "./HelperTypes.js";
|
|
9
9
|
import { $builder } from "./symbols.js";
|
|
10
10
|
import { type QuantizeOptions } from "./quantize.js";
|
|
11
11
|
/**
|
|
@@ -217,18 +217,22 @@ export type ChildType = RawPrimitiveType | Constructor<Schema>;
|
|
|
217
217
|
interface ArrayFactory {
|
|
218
218
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<ArraySchema<InstanceType<C>>, true, false>;
|
|
219
219
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<ArraySchema<InferValueType<P>>, true, false>;
|
|
220
|
+
<T>(child: CodecFor<T>): FieldBuilder<ArraySchema<T>, true, false>;
|
|
220
221
|
}
|
|
221
222
|
interface MapFactory {
|
|
222
223
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<MapSchema<InstanceType<C>>, true, false>;
|
|
223
224
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<MapSchema<InferValueType<P>>, true, false>;
|
|
225
|
+
<T>(child: CodecFor<T>): FieldBuilder<MapSchema<T>, true, false>;
|
|
224
226
|
}
|
|
225
227
|
interface SetFactory {
|
|
226
228
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<SetSchema<InstanceType<C>>, true, false>;
|
|
227
229
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<SetSchema<InferValueType<P>>, true, false>;
|
|
230
|
+
<T>(child: CodecFor<T>): FieldBuilder<SetSchema<T>, true, false>;
|
|
228
231
|
}
|
|
229
232
|
interface CollectionFactory {
|
|
230
233
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<CollectionSchema<InstanceType<C>>, true, false>;
|
|
231
234
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<CollectionSchema<InferValueType<P>>, true, false>;
|
|
235
|
+
<T>(child: CodecFor<T>): FieldBuilder<CollectionSchema<T>, true, false>;
|
|
232
236
|
}
|
|
233
237
|
interface StreamFactory {
|
|
234
238
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<StreamSchema<InstanceType<C>>, true, false>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colyseus/schema",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.22",
|
|
4
4
|
"description": "Automatic state replication for multiplayer games. Mutate plain objects, and every client gets a live, typed mirror through delta encoding.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/Metadata.ts
CHANGED
|
@@ -488,6 +488,10 @@ export const Metadata = {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
for (const field in fields) {
|
|
491
|
+
// metadata inherits the parent's, so this also catches a redeclared parent field
|
|
492
|
+
if (metadata[field] !== undefined) {
|
|
493
|
+
throw new Error(`@colyseus/schema: Duplicate '${field}' definition on '${constructor.name || "(anonymous)"}'.`);
|
|
494
|
+
}
|
|
491
495
|
Metadata.defineField(constructor, metadata, fieldIndex, field, fields[field] as DefinitionType);
|
|
492
496
|
fieldIndex++;
|
|
493
497
|
}
|
package/src/annotations.ts
CHANGED
|
@@ -626,11 +626,11 @@ export function defineTypes(
|
|
|
626
626
|
|
|
627
627
|
// Helper type to extract InitProps from initialize method.
|
|
628
628
|
// - Non-empty initialize params: use them directly.
|
|
629
|
-
// - Zero-arg initialize: no args accepted (`never`) —
|
|
630
|
-
// values would be dropped at runtime
|
|
631
|
-
// during child construction via the `new.target === klass` guard, and
|
|
632
|
-
// own-field auto-assignment happens only inside initialize).
|
|
629
|
+
// - Zero-arg initialize: no args accepted (`never`) — an initialize() owns the
|
|
630
|
+
// fields, so user-supplied values would be dropped at runtime.
|
|
633
631
|
// - No initialize at all: derive from fields map.
|
|
632
|
+
// `T & T2` from `.extend()` carries the parent's initialize unless the child
|
|
633
|
+
// redefines it, matching the runtime (the most-derived one runs).
|
|
634
634
|
type ExtractInitProps<T> = T extends { initialize: (...args: infer P) => void }
|
|
635
635
|
? P extends readonly []
|
|
636
636
|
? never
|
|
@@ -713,9 +713,7 @@ export interface SchemaWithExtendsConstructor<
|
|
|
713
713
|
: IsInitPropsRequired<T> extends true ? ([] | [InitProps])
|
|
714
714
|
: [InitProps?]
|
|
715
715
|
): SchemaInstance<T, P>;
|
|
716
|
-
prototype: SchemaInstance<T, P
|
|
717
|
-
initialize(...args: [InitProps] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : [InitProps]): void;
|
|
718
|
-
};
|
|
716
|
+
prototype: SchemaInstance<T, P>;
|
|
719
717
|
}
|
|
720
718
|
|
|
721
719
|
/**
|
|
@@ -742,6 +740,9 @@ function makeAutoDefaultFactory(rawType: any): (() => any) | undefined {
|
|
|
742
740
|
/**
|
|
743
741
|
* Define a Schema class declaratively.
|
|
744
742
|
*
|
|
743
|
+
* `initialize()` acts as the constructor: a class created with `.extend()`
|
|
744
|
+
* inherits the parent's unless it defines its own.
|
|
745
|
+
*
|
|
745
746
|
* @example
|
|
746
747
|
* import { schema, t } from '@colyseus/schema';
|
|
747
748
|
*
|
|
@@ -910,9 +911,12 @@ export function schema<
|
|
|
910
911
|
};
|
|
911
912
|
|
|
912
913
|
const hasInitialize = typeof methods.initialize === "function";
|
|
914
|
+
// Like a constructor: the most-derived initialize() runs once, own or
|
|
915
|
+
// inherited from a schema() parent. A custom base's initialize() is not
|
|
916
|
+
// picked up — the constructor type only sees the fields map.
|
|
917
|
+
const initialize = methods.initialize ?? (inherits as any)._initialize;
|
|
913
918
|
|
|
914
|
-
|
|
915
|
-
const klass = Metadata.setFields<any>(class extends (inherits as any) {
|
|
919
|
+
const klass = class extends (inherits as any) {
|
|
916
920
|
constructor(...args: any[]) {
|
|
917
921
|
const props = args[0];
|
|
918
922
|
if (props === undefined) {
|
|
@@ -926,14 +930,22 @@ export function schema<
|
|
|
926
930
|
// `initialize()` owns the schema fields, so only parent props flow up.
|
|
927
931
|
super(Object.assign(getDefaultValues(), hasInitialize ? getParentProps(props) : props));
|
|
928
932
|
}
|
|
929
|
-
// Only
|
|
930
|
-
if (
|
|
931
|
-
|
|
933
|
+
// Only on the exact target class — parents' constructors skip it.
|
|
934
|
+
if (initialize && new.target === klass) {
|
|
935
|
+
initialize.apply(this, args);
|
|
932
936
|
}
|
|
933
937
|
}
|
|
934
|
-
}
|
|
938
|
+
} as unknown as SchemaWithExtendsConstructor<T, ExtractInitProps<T>, P>;
|
|
939
|
+
|
|
940
|
+
// named before setFields so a duplicate-field error can say which class
|
|
941
|
+
if (name) {
|
|
942
|
+
Object.defineProperty(klass, "name", { value: name });
|
|
943
|
+
}
|
|
944
|
+
/** @codegen-ignore */
|
|
945
|
+
Metadata.setFields<any>(klass, fields);
|
|
935
946
|
|
|
936
947
|
(klass as any)._getDefaultValues = getDefaultValues;
|
|
948
|
+
(klass as any)._initialize = initialize;
|
|
937
949
|
|
|
938
950
|
Object.assign(klass.prototype, methods);
|
|
939
951
|
|
|
@@ -970,10 +982,6 @@ export function schema<
|
|
|
970
982
|
}
|
|
971
983
|
}
|
|
972
984
|
|
|
973
|
-
if (name) {
|
|
974
|
-
Object.defineProperty(klass, "name", { value: name });
|
|
975
|
-
}
|
|
976
|
-
|
|
977
985
|
(klass as any).extend = <T2 extends FieldsAndMethods = FieldsAndMethods>(
|
|
978
986
|
childFields: T2,
|
|
979
987
|
childName?: string,
|
package/src/types/HelperTypes.ts
CHANGED
|
@@ -14,6 +14,7 @@ export type Constructor<T = {}> = new (...args: any[]) => T;
|
|
|
14
14
|
type PrimitiveStringToType<T> =
|
|
15
15
|
T extends "string" ? string
|
|
16
16
|
: T extends "number" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" ? number
|
|
17
|
+
: T extends "bigint64" | "biguint64" ? bigint
|
|
17
18
|
: T extends "boolean" ? boolean
|
|
18
19
|
: T;
|
|
19
20
|
|
|
@@ -53,6 +54,8 @@ export type InferValueType<T> =
|
|
|
53
54
|
: T extends "uint64" ? number
|
|
54
55
|
: T extends "float32" ? number
|
|
55
56
|
: T extends "float64" ? number
|
|
57
|
+
: T extends "bigint64" ? bigint
|
|
58
|
+
: T extends "biguint64" ? bigint
|
|
56
59
|
: T extends "boolean" ? boolean
|
|
57
60
|
|
|
58
61
|
// Handle { type: ... } patterns
|
|
@@ -95,6 +98,19 @@ export type InferValueType<T> =
|
|
|
95
98
|
|
|
96
99
|
: never;
|
|
97
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Codecs that can carry a `T` — {@link InferValueType} run backwards, derived
|
|
103
|
+
* from it so the two can't drift. Constrains the element refinement in
|
|
104
|
+
* `t.array<Mark>("uint8")`; `never` (an uncallable overload) when no codec
|
|
105
|
+
* decodes into `T`.
|
|
106
|
+
*
|
|
107
|
+
* `[T] extends [...]` is deliberate: a distributive check would let a mixed
|
|
108
|
+
* union like `string | number` match on either half.
|
|
109
|
+
*/
|
|
110
|
+
export type CodecFor<T> = {
|
|
111
|
+
[K in RawPrimitiveType]: [T] extends [InferValueType<K>] ? K : never
|
|
112
|
+
}[RawPrimitiveType];
|
|
113
|
+
|
|
98
114
|
// Keys whose builder carries the `.optional()` brand. Reads the brand rather
|
|
99
115
|
// than `undefined extends V`: the latter is true for EVERY V when the consumer
|
|
100
116
|
// compiles with `strictNullChecks: false`, flipping all fields optional.
|
package/src/types/builder.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { CollectionSchema } from "./custom/CollectionSchema.js";
|
|
|
5
5
|
import type { StreamSchema } from "./custom/StreamSchema.js";
|
|
6
6
|
import type { Schema } from "../Schema.js";
|
|
7
7
|
import type { DefinitionType, RawPrimitiveType } from "../annotations.js";
|
|
8
|
-
import type { InferValueType, Constructor } from "./HelperTypes.js";
|
|
8
|
+
import type { InferValueType, Constructor, CodecFor } from "./HelperTypes.js";
|
|
9
9
|
import { $builder } from "./symbols.js";
|
|
10
10
|
import { ARRAY_STREAM_NOT_SUPPORTED } from "../encoder/streaming.js";
|
|
11
11
|
import { resolveQuantize, type QuantizeOptions } from "./quantize.js";
|
|
@@ -337,21 +337,28 @@ function resolveChild(child: ChildType): DefinitionType {
|
|
|
337
337
|
// overloads narrow the return type for Schema/primitive children.
|
|
338
338
|
// All collection factories tag `HasDefault = true` because schema() auto-
|
|
339
339
|
// instantiates an empty collection when no explicit default is given.
|
|
340
|
+
//
|
|
341
|
+
// The third overload refines the ELEMENT type (`t.map<V>` — the value, never
|
|
342
|
+
// the key), with the same type-level-only caveat as `PrimitiveFactory` above.
|
|
340
343
|
interface ArrayFactory {
|
|
341
344
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<ArraySchema<InstanceType<C>>, true, false>;
|
|
342
345
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<ArraySchema<InferValueType<P>>, true, false>;
|
|
346
|
+
<T>(child: CodecFor<T>): FieldBuilder<ArraySchema<T>, true, false>;
|
|
343
347
|
}
|
|
344
348
|
interface MapFactory {
|
|
345
349
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<MapSchema<InstanceType<C>>, true, false>;
|
|
346
350
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<MapSchema<InferValueType<P>>, true, false>;
|
|
351
|
+
<T>(child: CodecFor<T>): FieldBuilder<MapSchema<T>, true, false>;
|
|
347
352
|
}
|
|
348
353
|
interface SetFactory {
|
|
349
354
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<SetSchema<InstanceType<C>>, true, false>;
|
|
350
355
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<SetSchema<InferValueType<P>>, true, false>;
|
|
356
|
+
<T>(child: CodecFor<T>): FieldBuilder<SetSchema<T>, true, false>;
|
|
351
357
|
}
|
|
352
358
|
interface CollectionFactory {
|
|
353
359
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<CollectionSchema<InstanceType<C>>, true, false>;
|
|
354
360
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<CollectionSchema<InferValueType<P>>, true, false>;
|
|
361
|
+
<T>(child: CodecFor<T>): FieldBuilder<CollectionSchema<T>, true, false>;
|
|
355
362
|
}
|
|
356
363
|
// t.stream(Entity) — priority-batched collection of Schema instances.
|
|
357
364
|
// Element type is restricted to Schema subclasses (no primitives) because
|