@colyseus/schema 5.0.0 → 5.0.2

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.
@@ -12,6 +12,7 @@ export type MetadataField = {
12
12
  owned?: boolean;
13
13
  static?: boolean;
14
14
  stream?: boolean;
15
+ optional?: boolean;
15
16
  };
16
17
  export type Metadata = {
17
18
  [$numFields]: number;
@@ -30,36 +30,36 @@ interface ReflectionStatic {
30
30
  * Reflection
31
31
  */
32
32
  export declare const ReflectionField: import("./annotations.js").SchemaWithExtendsConstructor<{
33
- name: FieldBuilder<string>;
34
- type: FieldBuilder<string>;
35
- referencedType: FieldBuilder<number>;
36
- }, import("./index.js").AssignableProps<import("./index.js").InferSchemaInstanceType<{
37
- name: FieldBuilder<string>;
38
- type: FieldBuilder<string>;
39
- referencedType: FieldBuilder<number>;
40
- }>>, typeof Schema>;
33
+ name: FieldBuilder<string, false, false>;
34
+ type: FieldBuilder<string, false, false>;
35
+ referencedType: FieldBuilder<number, false, false>;
36
+ }, import("./index.js").BuilderInitProps<{
37
+ name: FieldBuilder<string, false, false>;
38
+ type: FieldBuilder<string, false, false>;
39
+ referencedType: FieldBuilder<number, false, false>;
40
+ }>, typeof Schema>;
41
41
  export type ReflectionField = SchemaType<typeof ReflectionField>;
42
42
  export declare const ReflectionType: import("./annotations.js").SchemaWithExtendsConstructor<{
43
- id: FieldBuilder<number>;
44
- extendsId: FieldBuilder<number>;
45
- fields: FieldBuilder<ArraySchema<{
46
- name: string;
47
- type: string;
48
- referencedType: number;
49
- } & Schema<any> & Schema<unknown>>>;
50
- }, import("./index.js").AssignableProps<import("./index.js").InferSchemaInstanceType<{
51
- id: FieldBuilder<number>;
52
- extendsId: FieldBuilder<number>;
53
- fields: FieldBuilder<ArraySchema<{
54
- name: string;
55
- type: string;
56
- referencedType: number;
57
- } & Schema<any> & Schema<unknown>>>;
58
- }>>, typeof Schema>;
43
+ id: FieldBuilder<number, false, false>;
44
+ extendsId: FieldBuilder<number, false, false>;
45
+ fields: FieldBuilder<ArraySchema<{} & {
46
+ name?: string;
47
+ type?: string;
48
+ referencedType?: number;
49
+ } & Schema<any> & Schema<unknown>>, true, false>;
50
+ }, import("./index.js").BuilderInitProps<{
51
+ id: FieldBuilder<number, false, false>;
52
+ extendsId: FieldBuilder<number, false, false>;
53
+ fields: FieldBuilder<ArraySchema<{} & {
54
+ name?: string;
55
+ type?: string;
56
+ referencedType?: number;
57
+ } & Schema<any> & Schema<unknown>>, true, false>;
58
+ }>, typeof Schema>;
59
59
  export type ReflectionType = SchemaType<typeof ReflectionType>;
60
60
  export declare const Reflection: ReturnType<typeof schema<{
61
- types: FieldBuilder<ArraySchema<ReflectionType>>;
62
- rootType: FieldBuilder<number>;
61
+ types: FieldBuilder<ArraySchema<ReflectionType>, true, false>;
62
+ rootType: FieldBuilder<number, false, false>;
63
63
  }>> & ReflectionStatic;
64
64
  export type Reflection = SchemaType<typeof Reflection>;
65
65
  export {};
@@ -3,7 +3,7 @@ import { Schema } from './Schema.js';
3
3
  import { ArraySchema } from './types/custom/ArraySchema.js';
4
4
  import { MapSchema } from './types/custom/MapSchema.js';
5
5
  import { TypeDefinition } from "./types/registry.js";
6
- import type { InferValueType, InferSchemaInstanceType, AssignableProps } from "./types/HelperTypes.js";
6
+ import type { InferValueType, InferSchemaInstanceType, BuilderInitProps } from "./types/HelperTypes.js";
7
7
  import { CollectionSchema } from "./types/custom/CollectionSchema.js";
8
8
  import { SetSchema } from "./types/custom/SetSchema.js";
9
9
  import { StreamSchema } from "./types/custom/StreamSchema.js";
@@ -98,19 +98,21 @@ export declare function getPropertyDescriptor(fieldName: string, fieldIndex: num
98
98
  export declare function deprecated(throws?: boolean): PropertyDecorator;
99
99
  type ExtractInitProps<T> = T extends {
100
100
  initialize: (...args: infer P) => void;
101
- } ? P extends readonly [] ? never : P extends readonly [infer First] ? First extends object ? First : P : P : AssignableProps<InferSchemaInstanceType<T>>;
101
+ } ? P extends readonly [] ? never : P extends readonly [infer First] ? First extends object ? First : P : P : BuilderInitProps<T>;
102
+ type HasRequiredKeys<X> = {} extends X ? false : true;
102
103
  type IsInitPropsRequired<T> = T extends {
103
- initialize: (props: any) => void;
104
- } ? true : T extends {
104
+ initialize: (...args: infer P) => void;
105
+ } ? P extends readonly [] ? false : true : HasRequiredKeys<BuilderInitProps<T>>;
106
+ type HasExplicitInit<T> = T extends {
105
107
  initialize: (...args: infer P) => void;
106
108
  } ? P extends readonly [] ? false : true : false;
107
109
  /**
108
110
  * A `schema()` field definition accepts a FieldBuilder, a Schema subclass
109
111
  * (shorthand for `t.ref(Class)`), or a method (attached to the prototype).
110
112
  */
111
- export type FieldsAndMethods = Record<string, FieldBuilder<any> | (new (...args: any[]) => Schema) | Function>;
113
+ export type FieldsAndMethods = Record<string, FieldBuilder<any, boolean, boolean> | (new (...args: any[]) => Schema) | Function>;
112
114
  export interface SchemaWithExtends<T, P extends typeof Schema> {
113
- extend: <T2 extends FieldsAndMethods = FieldsAndMethods>(fields: T2 & ThisType<InferSchemaInstanceType<T & T2>>, name?: string) => SchemaWithExtendsConstructor<T & T2, ExtractInitProps<T2>, P>;
115
+ extend: <T2 extends FieldsAndMethods = FieldsAndMethods>(fields: T2 & ThisType<InferSchemaInstanceType<T & T2>>, name?: string) => SchemaWithExtendsConstructor<T & T2, ExtractInitProps<T & T2>, P>;
114
116
  }
115
117
  /**
116
118
  * Get the type of the schema defined via `schema('Name', {...})` method.
@@ -127,7 +129,9 @@ export type SchemaType<T extends {
127
129
  }> = T['~type'];
128
130
  export interface SchemaWithExtendsConstructor<T, InitProps, P extends typeof Schema> extends SchemaWithExtends<T, P> {
129
131
  '~type': InferSchemaInstanceType<T>;
130
- new (...args: [InitProps] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : IsInitPropsRequired<T> extends true ? [InitProps] : [InitProps?]): InferSchemaInstanceType<T> & InstanceType<P>;
132
+ new (...args: [
133
+ InitProps
134
+ ] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : HasExplicitInit<T> extends true ? [InitProps] : IsInitPropsRequired<T> extends true ? ([] | [InitProps]) : [InitProps?]): InferSchemaInstanceType<T> & InstanceType<P>;
131
135
  prototype: InferSchemaInstanceType<T> & InstanceType<P> & {
132
136
  initialize(...args: [InitProps] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : [InitProps]): void;
133
137
  };
package/build/index.cjs CHANGED
@@ -28,7 +28,31 @@ exports.OPERATION = void 0;
28
28
 
29
29
  Symbol.metadata ??= Symbol.for("Symbol.metadata");
30
30
 
31
- const $refId = Symbol("$refId");
31
+ const _g = (function () {
32
+ if (typeof globalThis !== "undefined")
33
+ return globalThis;
34
+ if (typeof global !== "undefined")
35
+ return global;
36
+ if (typeof self !== "undefined")
37
+ return self;
38
+ if (typeof window !== "undefined")
39
+ return window;
40
+ return {};
41
+ })();
42
+ if (typeof Symbol === "function" && typeof Symbol.for !== "function") {
43
+ const REGISTRY_KEY = "colyseus.symbolRegistry";
44
+ const registry = _g[REGISTRY_KEY] || (_g[REGISTRY_KEY] = Object.create(null));
45
+ Symbol.for = function (key) {
46
+ return registry[key] || (registry[key] = Symbol(key));
47
+ };
48
+ Symbol.keyFor = function (sym) {
49
+ for (const k in registry)
50
+ if (registry[k] === sym)
51
+ return k;
52
+ return undefined;
53
+ };
54
+ }
55
+ const $refId = Symbol.for("$refId");
32
56
  const $track = "~track";
33
57
  const $encoder = "~encoder";
34
58
  const $decoder = "~decoder";
@@ -40,12 +64,12 @@ const $deleteByIndex = "~deleteByIndex";
40
64
  *
41
65
  * Real JS Symbol — see the `$values` comment for rationale.
42
66
  */
43
- const $changes = Symbol("$changes");
67
+ const $changes = Symbol.for("$changes");
44
68
  /**
45
69
  * Used to keep track of the type of the child elements of a collection
46
70
  * (MapSchema, ArraySchema, etc.). Real Symbol — same rationale as $values.
47
71
  */
48
- const $childType = Symbol("$childType");
72
+ const $childType = Symbol.for("$childType");
49
73
  /**
50
74
  * Self-reference an instance sets on `this` so its own methods can recover
51
75
  * the underlying object even when `this` is a Proxy wrapper. Used by
@@ -53,7 +77,7 @@ const $childType = Symbol("$childType");
53
77
  * once at the top of hot methods and then access fields directly without
54
78
  * paying the Proxy.get cost on every read.
55
79
  */
56
- const $proxyTarget = Symbol("$proxyTarget");
80
+ const $proxyTarget = Symbol.for("$proxyTarget");
57
81
  /**
58
82
  * Optional "discard" method for custom types (ArraySchema)
59
83
  * (Discards changes for next serialization)
@@ -72,7 +96,7 @@ const $onDecodeEnd = "~onDecodeEnd";
72
96
  * which means we can drop Object.defineProperty(...{ enumerable: false })
73
97
  * and avoid the slow-path / dictionary-mode hazards that come with it.
74
98
  */
75
- const $values = Symbol("$values");
99
+ const $values = Symbol.for("$values");
76
100
  /**
77
101
  * Brand for FieldBuilder instances so schema() can detect them.
78
102
  */
@@ -5386,9 +5410,23 @@ registerType("stream", { constructor: StreamSchema });
5386
5410
  /**
5387
5411
  * Chainable field builder. Instances are produced by `t.*()` factories.
5388
5412
  *
5389
- * The generic parameter T is the runtime/JS type of the field (e.g. `number`,
5390
- * `string`, `ArraySchema<Item>`). schema() reads the internal configuration
5391
- * via `toDefinition()` and wires up metadata through the existing pipeline.
5413
+ * Generics:
5414
+ * - `T` is the runtime/JS type of the field (e.g. `number`, `string`,
5415
+ * `ArraySchema<Item>`). `.optional()` widens it to `T | undefined`
5416
+ * so the inferred instance/toJSON shapes reflect absence.
5417
+ * - `HasDefault` is a compile-time flag that the field carries a
5418
+ * construction-time default — either an explicit `.default(v)` or an
5419
+ * auto-default from a collection factory (`t.array`, `t.map`, …) or a
5420
+ * Schema ref whose `initialize` takes zero args.
5421
+ * - `IsOptional` is a compile-time brand for `.optional()`. Both
5422
+ * `HasDefault` and `IsOptional` make the field omittable in
5423
+ * `BuilderInitProps<T>`. A separate brand (rather than reading
5424
+ * `undefined extends V`) sidesteps a TypeScript quirk where
5425
+ * class-generic-inferred `V` resolves `undefined extends V` as `true`
5426
+ * even for non-undefined types.
5427
+ *
5428
+ * schema() reads the internal configuration via `toDefinition()` and wires
5429
+ * up metadata through the existing pipeline.
5392
5430
  */
5393
5431
  class FieldBuilder {
5394
5432
  [$builder] = true;
@@ -5405,6 +5443,7 @@ class FieldBuilder {
5405
5443
  _deprecatedThrows = true;
5406
5444
  _static = false;
5407
5445
  _stream = false;
5446
+ _optional = false;
5408
5447
  _streamPriority = undefined;
5409
5448
  constructor(type) {
5410
5449
  this._type = type;
@@ -5499,6 +5538,16 @@ class FieldBuilder {
5499
5538
  this._deprecatedThrows = throws;
5500
5539
  return this;
5501
5540
  }
5541
+ /**
5542
+ * Mark this field as optional — inferred instance type becomes
5543
+ * `T | undefined` and the property becomes omittable in initialization
5544
+ * props. Skips the auto-instantiation of collection / Schema-ref
5545
+ * defaults, so the field starts as `undefined` at runtime.
5546
+ */
5547
+ optional() {
5548
+ this._optional = true;
5549
+ return this;
5550
+ }
5502
5551
  toDefinition() {
5503
5552
  return {
5504
5553
  type: this._type,
@@ -5512,6 +5561,7 @@ class FieldBuilder {
5512
5561
  deprecatedThrows: this._deprecatedThrows,
5513
5562
  static: this._static,
5514
5563
  stream: this._stream,
5564
+ optional: this._optional,
5515
5565
  streamPriority: this._streamPriority,
5516
5566
  };
5517
5567
  }
@@ -5540,9 +5590,7 @@ const streamFactory = ((child) => {
5540
5590
  b._stream = true;
5541
5591
  return b;
5542
5592
  });
5543
- function refFactory(ctor) {
5544
- return new FieldBuilder(ctor);
5545
- }
5593
+ const refFactory = ((ctor) => new FieldBuilder(ctor));
5546
5594
  const t = Object.freeze({
5547
5595
  // Primitives
5548
5596
  string: primitive("string"),
@@ -5999,6 +6047,7 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
5999
6047
  const staticFields = [];
6000
6048
  const streamFields = [];
6001
6049
  const streamPriorityFields = {};
6050
+ const optionalFields = [];
6002
6051
  for (const fieldName in fieldsAndMethods) {
6003
6052
  const value = fieldsAndMethods[fieldName];
6004
6053
  if (isBuilder(value)) {
@@ -6028,11 +6077,15 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
6028
6077
  if (def.streamPriority !== undefined) {
6029
6078
  streamPriorityFields[fieldName] = def.streamPriority;
6030
6079
  }
6080
+ if (def.optional) {
6081
+ optionalFields.push(fieldName);
6082
+ }
6031
6083
  if (def.hasDefault) {
6032
6084
  defaultValues[fieldName] = def.default;
6033
6085
  }
6034
- else {
6086
+ else if (!def.optional) {
6035
6087
  // Auto-instantiate collection/Schema defaults when none is provided.
6088
+ // `.optional()` opts out — field starts as undefined.
6036
6089
  const rawType = def.type;
6037
6090
  if (rawType && typeof rawType === "object") {
6038
6091
  if (rawType.array !== undefined) {
@@ -6142,6 +6195,12 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
6142
6195
  Metadata.setStreamPriority(metadata, fieldName, streamPriorityFields[fieldName]);
6143
6196
  }
6144
6197
  }
6198
+ if (optionalFields.length > 0) {
6199
+ const metadata = klass[Symbol.metadata];
6200
+ for (const fieldName of optionalFields) {
6201
+ metadata[metadata[fieldName]].optional = true;
6202
+ }
6203
+ }
6145
6204
  if (name) {
6146
6205
  Object.defineProperty(klass, "name", { value: name });
6147
6206
  }