@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.
package/build/index.d.ts CHANGED
@@ -22,7 +22,7 @@ export { Metadata } from "./Metadata.js";
22
22
  export { type, deprecated, owned, unreliable, transient, view, schema, entity, type DefinitionType, type PrimitiveType, type Definition, type FieldsAndMethods, type SchemaWithExtendsConstructor, type SchemaWithExtends, type SchemaType, } from "./annotations.js";
23
23
  export { t, FieldBuilder, isBuilder, type BuilderDefinition, type ChildType } from "./types/builder.js";
24
24
  export { TypeContext } from "./types/TypeContext.js";
25
- export type { InferValueType, InferSchemaInstanceType, AssignableProps } from "./types/HelperTypes.js";
25
+ export type { InferValueType, InferSchemaInstanceType, AssignableProps, BuilderInitProps } from "./types/HelperTypes.js";
26
26
  export { getDecoderStateCallbacks, type CallbackProxy, type SchemaCallback, type CollectionCallback, type SchemaCallbackProxy } from "./decoder/strategy/getDecoderStateCallbacks.js";
27
27
  export { Callbacks, StateCallbackStrategy } from "./decoder/strategy/Callbacks.js";
28
28
  export { getRawChangesCallback } from "./decoder/strategy/RawChanges.js";
package/build/index.js CHANGED
@@ -32,7 +32,31 @@
32
32
 
33
33
  Symbol.metadata ??= Symbol.for("Symbol.metadata");
34
34
 
35
- const $refId = Symbol("$refId");
35
+ const _g = (function () {
36
+ if (typeof globalThis !== "undefined")
37
+ return globalThis;
38
+ if (typeof global !== "undefined")
39
+ return global;
40
+ if (typeof self !== "undefined")
41
+ return self;
42
+ if (typeof window !== "undefined")
43
+ return window;
44
+ return {};
45
+ })();
46
+ if (typeof Symbol === "function" && typeof Symbol.for !== "function") {
47
+ const REGISTRY_KEY = "colyseus.symbolRegistry";
48
+ const registry = _g[REGISTRY_KEY] || (_g[REGISTRY_KEY] = Object.create(null));
49
+ Symbol.for = function (key) {
50
+ return registry[key] || (registry[key] = Symbol(key));
51
+ };
52
+ Symbol.keyFor = function (sym) {
53
+ for (const k in registry)
54
+ if (registry[k] === sym)
55
+ return k;
56
+ return undefined;
57
+ };
58
+ }
59
+ const $refId = Symbol.for("$refId");
36
60
  const $track = "~track";
37
61
  const $encoder = "~encoder";
38
62
  const $decoder = "~decoder";
@@ -44,12 +68,12 @@
44
68
  *
45
69
  * Real JS Symbol — see the `$values` comment for rationale.
46
70
  */
47
- const $changes = Symbol("$changes");
71
+ const $changes = Symbol.for("$changes");
48
72
  /**
49
73
  * Used to keep track of the type of the child elements of a collection
50
74
  * (MapSchema, ArraySchema, etc.). Real Symbol — same rationale as $values.
51
75
  */
52
- const $childType = Symbol("$childType");
76
+ const $childType = Symbol.for("$childType");
53
77
  /**
54
78
  * Self-reference an instance sets on `this` so its own methods can recover
55
79
  * the underlying object even when `this` is a Proxy wrapper. Used by
@@ -57,7 +81,7 @@
57
81
  * once at the top of hot methods and then access fields directly without
58
82
  * paying the Proxy.get cost on every read.
59
83
  */
60
- const $proxyTarget = Symbol("$proxyTarget");
84
+ const $proxyTarget = Symbol.for("$proxyTarget");
61
85
  /**
62
86
  * Optional "discard" method for custom types (ArraySchema)
63
87
  * (Discards changes for next serialization)
@@ -76,7 +100,7 @@
76
100
  * which means we can drop Object.defineProperty(...{ enumerable: false })
77
101
  * and avoid the slow-path / dictionary-mode hazards that come with it.
78
102
  */
79
- const $values = Symbol("$values");
103
+ const $values = Symbol.for("$values");
80
104
  /**
81
105
  * Brand for FieldBuilder instances so schema() can detect them.
82
106
  */
@@ -5390,9 +5414,23 @@
5390
5414
  /**
5391
5415
  * Chainable field builder. Instances are produced by `t.*()` factories.
5392
5416
  *
5393
- * The generic parameter T is the runtime/JS type of the field (e.g. `number`,
5394
- * `string`, `ArraySchema<Item>`). schema() reads the internal configuration
5395
- * via `toDefinition()` and wires up metadata through the existing pipeline.
5417
+ * Generics:
5418
+ * - `T` is the runtime/JS type of the field (e.g. `number`, `string`,
5419
+ * `ArraySchema<Item>`). `.optional()` widens it to `T | undefined`
5420
+ * so the inferred instance/toJSON shapes reflect absence.
5421
+ * - `HasDefault` is a compile-time flag that the field carries a
5422
+ * construction-time default — either an explicit `.default(v)` or an
5423
+ * auto-default from a collection factory (`t.array`, `t.map`, …) or a
5424
+ * Schema ref whose `initialize` takes zero args.
5425
+ * - `IsOptional` is a compile-time brand for `.optional()`. Both
5426
+ * `HasDefault` and `IsOptional` make the field omittable in
5427
+ * `BuilderInitProps<T>`. A separate brand (rather than reading
5428
+ * `undefined extends V`) sidesteps a TypeScript quirk where
5429
+ * class-generic-inferred `V` resolves `undefined extends V` as `true`
5430
+ * even for non-undefined types.
5431
+ *
5432
+ * schema() reads the internal configuration via `toDefinition()` and wires
5433
+ * up metadata through the existing pipeline.
5396
5434
  */
5397
5435
  class FieldBuilder {
5398
5436
  [$builder] = true;
@@ -5409,6 +5447,7 @@
5409
5447
  _deprecatedThrows = true;
5410
5448
  _static = false;
5411
5449
  _stream = false;
5450
+ _optional = false;
5412
5451
  _streamPriority = undefined;
5413
5452
  constructor(type) {
5414
5453
  this._type = type;
@@ -5503,6 +5542,16 @@
5503
5542
  this._deprecatedThrows = throws;
5504
5543
  return this;
5505
5544
  }
5545
+ /**
5546
+ * Mark this field as optional — inferred instance type becomes
5547
+ * `T | undefined` and the property becomes omittable in initialization
5548
+ * props. Skips the auto-instantiation of collection / Schema-ref
5549
+ * defaults, so the field starts as `undefined` at runtime.
5550
+ */
5551
+ optional() {
5552
+ this._optional = true;
5553
+ return this;
5554
+ }
5506
5555
  toDefinition() {
5507
5556
  return {
5508
5557
  type: this._type,
@@ -5516,6 +5565,7 @@
5516
5565
  deprecatedThrows: this._deprecatedThrows,
5517
5566
  static: this._static,
5518
5567
  stream: this._stream,
5568
+ optional: this._optional,
5519
5569
  streamPriority: this._streamPriority,
5520
5570
  };
5521
5571
  }
@@ -5544,9 +5594,7 @@
5544
5594
  b._stream = true;
5545
5595
  return b;
5546
5596
  });
5547
- function refFactory(ctor) {
5548
- return new FieldBuilder(ctor);
5549
- }
5597
+ const refFactory = ((ctor) => new FieldBuilder(ctor));
5550
5598
  const t = Object.freeze({
5551
5599
  // Primitives
5552
5600
  string: primitive("string"),
@@ -6003,6 +6051,7 @@
6003
6051
  const staticFields = [];
6004
6052
  const streamFields = [];
6005
6053
  const streamPriorityFields = {};
6054
+ const optionalFields = [];
6006
6055
  for (const fieldName in fieldsAndMethods) {
6007
6056
  const value = fieldsAndMethods[fieldName];
6008
6057
  if (isBuilder(value)) {
@@ -6032,11 +6081,15 @@
6032
6081
  if (def.streamPriority !== undefined) {
6033
6082
  streamPriorityFields[fieldName] = def.streamPriority;
6034
6083
  }
6084
+ if (def.optional) {
6085
+ optionalFields.push(fieldName);
6086
+ }
6035
6087
  if (def.hasDefault) {
6036
6088
  defaultValues[fieldName] = def.default;
6037
6089
  }
6038
- else {
6090
+ else if (!def.optional) {
6039
6091
  // Auto-instantiate collection/Schema defaults when none is provided.
6092
+ // `.optional()` opts out — field starts as undefined.
6040
6093
  const rawType = def.type;
6041
6094
  if (rawType && typeof rawType === "object") {
6042
6095
  if (rawType.array !== undefined) {
@@ -6146,6 +6199,12 @@
6146
6199
  Metadata.setStreamPriority(metadata, fieldName, streamPriorityFields[fieldName]);
6147
6200
  }
6148
6201
  }
6202
+ if (optionalFields.length > 0) {
6203
+ const metadata = klass[Symbol.metadata];
6204
+ for (const fieldName of optionalFields) {
6205
+ metadata[metadata[fieldName]].optional = true;
6206
+ }
6207
+ }
6149
6208
  if (name) {
6150
6209
  Object.defineProperty(klass, "name", { value: name });
6151
6210
  }
package/build/index.mjs CHANGED
@@ -26,7 +26,31 @@ var OPERATION;
26
26
 
27
27
  Symbol.metadata ??= Symbol.for("Symbol.metadata");
28
28
 
29
- const $refId = Symbol("$refId");
29
+ const _g = (function () {
30
+ if (typeof globalThis !== "undefined")
31
+ return globalThis;
32
+ if (typeof global !== "undefined")
33
+ return global;
34
+ if (typeof self !== "undefined")
35
+ return self;
36
+ if (typeof window !== "undefined")
37
+ return window;
38
+ return {};
39
+ })();
40
+ if (typeof Symbol === "function" && typeof Symbol.for !== "function") {
41
+ const REGISTRY_KEY = "colyseus.symbolRegistry";
42
+ const registry = _g[REGISTRY_KEY] || (_g[REGISTRY_KEY] = Object.create(null));
43
+ Symbol.for = function (key) {
44
+ return registry[key] || (registry[key] = Symbol(key));
45
+ };
46
+ Symbol.keyFor = function (sym) {
47
+ for (const k in registry)
48
+ if (registry[k] === sym)
49
+ return k;
50
+ return undefined;
51
+ };
52
+ }
53
+ const $refId = Symbol.for("$refId");
30
54
  const $track = "~track";
31
55
  const $encoder = "~encoder";
32
56
  const $decoder = "~decoder";
@@ -38,12 +62,12 @@ const $deleteByIndex = "~deleteByIndex";
38
62
  *
39
63
  * Real JS Symbol — see the `$values` comment for rationale.
40
64
  */
41
- const $changes = Symbol("$changes");
65
+ const $changes = Symbol.for("$changes");
42
66
  /**
43
67
  * Used to keep track of the type of the child elements of a collection
44
68
  * (MapSchema, ArraySchema, etc.). Real Symbol — same rationale as $values.
45
69
  */
46
- const $childType = Symbol("$childType");
70
+ const $childType = Symbol.for("$childType");
47
71
  /**
48
72
  * Self-reference an instance sets on `this` so its own methods can recover
49
73
  * the underlying object even when `this` is a Proxy wrapper. Used by
@@ -51,7 +75,7 @@ const $childType = Symbol("$childType");
51
75
  * once at the top of hot methods and then access fields directly without
52
76
  * paying the Proxy.get cost on every read.
53
77
  */
54
- const $proxyTarget = Symbol("$proxyTarget");
78
+ const $proxyTarget = Symbol.for("$proxyTarget");
55
79
  /**
56
80
  * Optional "discard" method for custom types (ArraySchema)
57
81
  * (Discards changes for next serialization)
@@ -70,7 +94,7 @@ const $onDecodeEnd = "~onDecodeEnd";
70
94
  * which means we can drop Object.defineProperty(...{ enumerable: false })
71
95
  * and avoid the slow-path / dictionary-mode hazards that come with it.
72
96
  */
73
- const $values = Symbol("$values");
97
+ const $values = Symbol.for("$values");
74
98
  /**
75
99
  * Brand for FieldBuilder instances so schema() can detect them.
76
100
  */
@@ -5384,9 +5408,23 @@ registerType("stream", { constructor: StreamSchema });
5384
5408
  /**
5385
5409
  * Chainable field builder. Instances are produced by `t.*()` factories.
5386
5410
  *
5387
- * The generic parameter T is the runtime/JS type of the field (e.g. `number`,
5388
- * `string`, `ArraySchema<Item>`). schema() reads the internal configuration
5389
- * via `toDefinition()` and wires up metadata through the existing pipeline.
5411
+ * Generics:
5412
+ * - `T` is the runtime/JS type of the field (e.g. `number`, `string`,
5413
+ * `ArraySchema<Item>`). `.optional()` widens it to `T | undefined`
5414
+ * so the inferred instance/toJSON shapes reflect absence.
5415
+ * - `HasDefault` is a compile-time flag that the field carries a
5416
+ * construction-time default — either an explicit `.default(v)` or an
5417
+ * auto-default from a collection factory (`t.array`, `t.map`, …) or a
5418
+ * Schema ref whose `initialize` takes zero args.
5419
+ * - `IsOptional` is a compile-time brand for `.optional()`. Both
5420
+ * `HasDefault` and `IsOptional` make the field omittable in
5421
+ * `BuilderInitProps<T>`. A separate brand (rather than reading
5422
+ * `undefined extends V`) sidesteps a TypeScript quirk where
5423
+ * class-generic-inferred `V` resolves `undefined extends V` as `true`
5424
+ * even for non-undefined types.
5425
+ *
5426
+ * schema() reads the internal configuration via `toDefinition()` and wires
5427
+ * up metadata through the existing pipeline.
5390
5428
  */
5391
5429
  class FieldBuilder {
5392
5430
  [$builder] = true;
@@ -5403,6 +5441,7 @@ class FieldBuilder {
5403
5441
  _deprecatedThrows = true;
5404
5442
  _static = false;
5405
5443
  _stream = false;
5444
+ _optional = false;
5406
5445
  _streamPriority = undefined;
5407
5446
  constructor(type) {
5408
5447
  this._type = type;
@@ -5497,6 +5536,16 @@ class FieldBuilder {
5497
5536
  this._deprecatedThrows = throws;
5498
5537
  return this;
5499
5538
  }
5539
+ /**
5540
+ * Mark this field as optional — inferred instance type becomes
5541
+ * `T | undefined` and the property becomes omittable in initialization
5542
+ * props. Skips the auto-instantiation of collection / Schema-ref
5543
+ * defaults, so the field starts as `undefined` at runtime.
5544
+ */
5545
+ optional() {
5546
+ this._optional = true;
5547
+ return this;
5548
+ }
5500
5549
  toDefinition() {
5501
5550
  return {
5502
5551
  type: this._type,
@@ -5510,6 +5559,7 @@ class FieldBuilder {
5510
5559
  deprecatedThrows: this._deprecatedThrows,
5511
5560
  static: this._static,
5512
5561
  stream: this._stream,
5562
+ optional: this._optional,
5513
5563
  streamPriority: this._streamPriority,
5514
5564
  };
5515
5565
  }
@@ -5538,9 +5588,7 @@ const streamFactory = ((child) => {
5538
5588
  b._stream = true;
5539
5589
  return b;
5540
5590
  });
5541
- function refFactory(ctor) {
5542
- return new FieldBuilder(ctor);
5543
- }
5591
+ const refFactory = ((ctor) => new FieldBuilder(ctor));
5544
5592
  const t = Object.freeze({
5545
5593
  // Primitives
5546
5594
  string: primitive("string"),
@@ -5997,6 +6045,7 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
5997
6045
  const staticFields = [];
5998
6046
  const streamFields = [];
5999
6047
  const streamPriorityFields = {};
6048
+ const optionalFields = [];
6000
6049
  for (const fieldName in fieldsAndMethods) {
6001
6050
  const value = fieldsAndMethods[fieldName];
6002
6051
  if (isBuilder(value)) {
@@ -6026,11 +6075,15 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
6026
6075
  if (def.streamPriority !== undefined) {
6027
6076
  streamPriorityFields[fieldName] = def.streamPriority;
6028
6077
  }
6078
+ if (def.optional) {
6079
+ optionalFields.push(fieldName);
6080
+ }
6029
6081
  if (def.hasDefault) {
6030
6082
  defaultValues[fieldName] = def.default;
6031
6083
  }
6032
- else {
6084
+ else if (!def.optional) {
6033
6085
  // Auto-instantiate collection/Schema defaults when none is provided.
6086
+ // `.optional()` opts out — field starts as undefined.
6034
6087
  const rawType = def.type;
6035
6088
  if (rawType && typeof rawType === "object") {
6036
6089
  if (rawType.array !== undefined) {
@@ -6140,6 +6193,12 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
6140
6193
  Metadata.setStreamPriority(metadata, fieldName, streamPriorityFields[fieldName]);
6141
6194
  }
6142
6195
  }
6196
+ if (optionalFields.length > 0) {
6197
+ const metadata = klass[Symbol.metadata];
6198
+ for (const fieldName of optionalFields) {
6199
+ metadata[metadata[fieldName]].optional = true;
6200
+ }
6201
+ }
6143
6202
  if (name) {
6144
6203
  Object.defineProperty(klass, "name", { value: name });
6145
6204
  }