@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/Metadata.d.ts +1 -0
- package/build/Reflection.d.ts +26 -26
- package/build/annotations.d.ts +11 -7
- package/build/index.cjs +71 -12
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.js +71 -12
- package/build/index.mjs +71 -12
- package/build/index.mjs.map +1 -1
- package/build/input/index.cjs +29 -5
- package/build/input/index.cjs.map +1 -1
- package/build/input/index.mjs +29 -5
- package/build/input/index.mjs.map +1 -1
- package/build/types/HelperTypes.d.ts +47 -3
- package/build/types/builder.d.ts +65 -35
- package/package.json +1 -1
- package/src/Metadata.ts +1 -0
- package/src/Reflection.ts +2 -2
- package/src/annotations.ts +58 -17
- package/src/index.ts +1 -1
- package/src/types/HelperTypes.ts +104 -19
- package/src/types/builder.ts +67 -21
- package/src/types/symbols.ts +45 -6
package/src/types/builder.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface BuilderDefinition {
|
|
|
26
26
|
deprecatedThrows?: boolean;
|
|
27
27
|
static?: boolean;
|
|
28
28
|
stream?: boolean;
|
|
29
|
+
optional?: boolean;
|
|
29
30
|
/** Declaration-scope priority callback for `.stream()` fields. */
|
|
30
31
|
streamPriority?: (view: any, element: any) => number;
|
|
31
32
|
}
|
|
@@ -38,11 +39,29 @@ export type BuilderOf<T> = FieldBuilder<T>;
|
|
|
38
39
|
/**
|
|
39
40
|
* Chainable field builder. Instances are produced by `t.*()` factories.
|
|
40
41
|
*
|
|
41
|
-
*
|
|
42
|
-
* `
|
|
43
|
-
*
|
|
42
|
+
* Generics:
|
|
43
|
+
* - `T` is the runtime/JS type of the field (e.g. `number`, `string`,
|
|
44
|
+
* `ArraySchema<Item>`). `.optional()` widens it to `T | undefined`
|
|
45
|
+
* so the inferred instance/toJSON shapes reflect absence.
|
|
46
|
+
* - `HasDefault` is a compile-time flag that the field carries a
|
|
47
|
+
* construction-time default — either an explicit `.default(v)` or an
|
|
48
|
+
* auto-default from a collection factory (`t.array`, `t.map`, …) or a
|
|
49
|
+
* Schema ref whose `initialize` takes zero args.
|
|
50
|
+
* - `IsOptional` is a compile-time brand for `.optional()`. Both
|
|
51
|
+
* `HasDefault` and `IsOptional` make the field omittable in
|
|
52
|
+
* `BuilderInitProps<T>`. A separate brand (rather than reading
|
|
53
|
+
* `undefined extends V`) sidesteps a TypeScript quirk where
|
|
54
|
+
* class-generic-inferred `V` resolves `undefined extends V` as `true`
|
|
55
|
+
* even for non-undefined types.
|
|
56
|
+
*
|
|
57
|
+
* schema() reads the internal configuration via `toDefinition()` and wires
|
|
58
|
+
* up metadata through the existing pipeline.
|
|
44
59
|
*/
|
|
45
|
-
export class FieldBuilder<
|
|
60
|
+
export class FieldBuilder<
|
|
61
|
+
T = unknown,
|
|
62
|
+
HasDefault extends boolean = false,
|
|
63
|
+
IsOptional extends boolean = false,
|
|
64
|
+
> {
|
|
46
65
|
readonly [$builder]: true = true;
|
|
47
66
|
|
|
48
67
|
// Internal configuration. Public so schema() and tests can read it, but not
|
|
@@ -58,6 +77,7 @@ export class FieldBuilder<T = unknown> {
|
|
|
58
77
|
_deprecatedThrows = true;
|
|
59
78
|
_static = false;
|
|
60
79
|
_stream = false;
|
|
80
|
+
_optional = false;
|
|
61
81
|
_streamPriority: ((view: any, element: any) => number) | undefined = undefined;
|
|
62
82
|
|
|
63
83
|
constructor(type: DefinitionType) {
|
|
@@ -65,10 +85,10 @@ export class FieldBuilder<T = unknown> {
|
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
/** Provide a default value for this field. */
|
|
68
|
-
default(value: T):
|
|
88
|
+
default(value: T): FieldBuilder<T, true, IsOptional> {
|
|
69
89
|
this._default = value;
|
|
70
90
|
this._hasDefault = true;
|
|
71
|
-
return this
|
|
91
|
+
return this as unknown as FieldBuilder<T, true, IsOptional>;
|
|
72
92
|
}
|
|
73
93
|
|
|
74
94
|
/** Tag this field with a view tag (DEFAULT_VIEW_TAG when called without arg). */
|
|
@@ -163,6 +183,17 @@ export class FieldBuilder<T = unknown> {
|
|
|
163
183
|
return this;
|
|
164
184
|
}
|
|
165
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Mark this field as optional — inferred instance type becomes
|
|
188
|
+
* `T | undefined` and the property becomes omittable in initialization
|
|
189
|
+
* props. Skips the auto-instantiation of collection / Schema-ref
|
|
190
|
+
* defaults, so the field starts as `undefined` at runtime.
|
|
191
|
+
*/
|
|
192
|
+
optional(): FieldBuilder<T | undefined, HasDefault, true> {
|
|
193
|
+
this._optional = true;
|
|
194
|
+
return this as unknown as FieldBuilder<T | undefined, HasDefault, true>;
|
|
195
|
+
}
|
|
196
|
+
|
|
166
197
|
toDefinition(): BuilderDefinition {
|
|
167
198
|
return {
|
|
168
199
|
type: this._type,
|
|
@@ -176,6 +207,7 @@ export class FieldBuilder<T = unknown> {
|
|
|
176
207
|
deprecatedThrows: this._deprecatedThrows,
|
|
177
208
|
static: this._static,
|
|
178
209
|
stream: this._stream,
|
|
210
|
+
optional: this._optional,
|
|
179
211
|
streamPriority: this._streamPriority,
|
|
180
212
|
};
|
|
181
213
|
}
|
|
@@ -212,31 +244,33 @@ function resolveChild(child: ChildType): DefinitionType {
|
|
|
212
244
|
|
|
213
245
|
// Overloaded factories for collections. Implementation lives in a single function;
|
|
214
246
|
// overloads narrow the return type for Schema/primitive/builder children.
|
|
247
|
+
// All collection factories tag `HasDefault = true` because schema() auto-
|
|
248
|
+
// instantiates an empty collection when no explicit default is given.
|
|
215
249
|
interface ArrayFactory {
|
|
216
|
-
<C extends Constructor<Schema>>(child: C): FieldBuilder<ArraySchema<InstanceType<C
|
|
217
|
-
<P extends RawPrimitiveType>(child: P): FieldBuilder<ArraySchema<InferValueType<P
|
|
218
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<ArraySchema<V
|
|
250
|
+
<C extends Constructor<Schema>>(child: C): FieldBuilder<ArraySchema<InstanceType<C>>, true, false>;
|
|
251
|
+
<P extends RawPrimitiveType>(child: P): FieldBuilder<ArraySchema<InferValueType<P>>, true, false>;
|
|
252
|
+
<V>(child: FieldBuilder<V>): FieldBuilder<ArraySchema<V>, true, false>;
|
|
219
253
|
}
|
|
220
254
|
interface MapFactory {
|
|
221
|
-
<C extends Constructor<Schema>>(child: C): FieldBuilder<MapSchema<InstanceType<C
|
|
222
|
-
<P extends RawPrimitiveType>(child: P): FieldBuilder<MapSchema<InferValueType<P
|
|
223
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<MapSchema<V
|
|
255
|
+
<C extends Constructor<Schema>>(child: C): FieldBuilder<MapSchema<InstanceType<C>>, true, false>;
|
|
256
|
+
<P extends RawPrimitiveType>(child: P): FieldBuilder<MapSchema<InferValueType<P>>, true, false>;
|
|
257
|
+
<V>(child: FieldBuilder<V>): FieldBuilder<MapSchema<V>, true, false>;
|
|
224
258
|
}
|
|
225
259
|
interface SetFactory {
|
|
226
|
-
<C extends Constructor<Schema>>(child: C): FieldBuilder<SetSchema<InstanceType<C
|
|
227
|
-
<P extends RawPrimitiveType>(child: P): FieldBuilder<SetSchema<InferValueType<P
|
|
228
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<SetSchema<V
|
|
260
|
+
<C extends Constructor<Schema>>(child: C): FieldBuilder<SetSchema<InstanceType<C>>, true, false>;
|
|
261
|
+
<P extends RawPrimitiveType>(child: P): FieldBuilder<SetSchema<InferValueType<P>>, true, false>;
|
|
262
|
+
<V>(child: FieldBuilder<V>): FieldBuilder<SetSchema<V>, true, false>;
|
|
229
263
|
}
|
|
230
264
|
interface CollectionFactory {
|
|
231
|
-
<C extends Constructor<Schema>>(child: C): FieldBuilder<CollectionSchema<InstanceType<C
|
|
232
|
-
<P extends RawPrimitiveType>(child: P): FieldBuilder<CollectionSchema<InferValueType<P
|
|
233
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<CollectionSchema<V
|
|
265
|
+
<C extends Constructor<Schema>>(child: C): FieldBuilder<CollectionSchema<InstanceType<C>>, true, false>;
|
|
266
|
+
<P extends RawPrimitiveType>(child: P): FieldBuilder<CollectionSchema<InferValueType<P>>, true, false>;
|
|
267
|
+
<V>(child: FieldBuilder<V>): FieldBuilder<CollectionSchema<V>, true, false>;
|
|
234
268
|
}
|
|
235
269
|
// t.stream(Entity) — priority-batched collection of Schema instances.
|
|
236
270
|
// Element type is restricted to Schema subclasses (no primitives) because
|
|
237
271
|
// priority batching relies on stable refIds, which primitives don't carry.
|
|
238
272
|
interface StreamFactory {
|
|
239
|
-
<C extends Constructor<Schema>>(child: C): FieldBuilder<StreamSchema<InstanceType<C
|
|
273
|
+
<C extends Constructor<Schema>>(child: C): FieldBuilder<StreamSchema<InstanceType<C>>, true, false>;
|
|
240
274
|
}
|
|
241
275
|
|
|
242
276
|
const arrayFactory: ArrayFactory = ((child: ChildType) =>
|
|
@@ -253,10 +287,22 @@ const streamFactory: StreamFactory = ((child: ChildType) => {
|
|
|
253
287
|
return b;
|
|
254
288
|
}) as StreamFactory;
|
|
255
289
|
|
|
256
|
-
|
|
257
|
-
|
|
290
|
+
// Compile-time: does this Schema subclass need arguments at construction?
|
|
291
|
+
// A zero-arg or absent `initialize(...)` means schema() will auto-default the
|
|
292
|
+
// field to `new X()`, so `HasDefault = true`. A non-zero-arg initialize means
|
|
293
|
+
// the user has to provide the ref explicitly.
|
|
294
|
+
type RefHasDefault<C> =
|
|
295
|
+
C extends { prototype: { initialize(...args: infer P): any } }
|
|
296
|
+
? (P extends readonly [] ? true : false)
|
|
297
|
+
: true;
|
|
298
|
+
|
|
299
|
+
interface RefFactory {
|
|
300
|
+
<C extends Constructor<Schema>>(ctor: C): FieldBuilder<InstanceType<C>, RefHasDefault<C>, false>;
|
|
258
301
|
}
|
|
259
302
|
|
|
303
|
+
const refFactory: RefFactory = (<C extends Constructor<Schema>>(ctor: C) =>
|
|
304
|
+
new FieldBuilder<InstanceType<C>>(ctor as unknown as DefinitionType)) as RefFactory;
|
|
305
|
+
|
|
260
306
|
export const t = Object.freeze({
|
|
261
307
|
// Primitives
|
|
262
308
|
string: primitive<string>("string"),
|
package/src/types/symbols.ts
CHANGED
|
@@ -1,4 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
//
|
|
2
|
+
// Cross-bundle symbol sharing.
|
|
3
|
+
//
|
|
4
|
+
// When @colyseus/schema is loaded more than once into the same JS realm
|
|
5
|
+
// (e.g. main bundle + ./input subpath bundle), plain `Symbol("$x")` calls
|
|
6
|
+
// in each copy produce distinct values — breaking identity checks like
|
|
7
|
+
// `instance[$values]` when the reader and the writer come from different
|
|
8
|
+
// copies.
|
|
9
|
+
//
|
|
10
|
+
// `Symbol.for(key)` resolves to the engine's process-wide Symbol Registry,
|
|
11
|
+
// so every copy gets the same symbol regardless of which one created it.
|
|
12
|
+
//
|
|
13
|
+
// Fallback: runtimes that lack `Symbol.for` get a polyfill anchored on
|
|
14
|
+
// globalThis (also shared across module copies).
|
|
15
|
+
//
|
|
16
|
+
declare const self: any;
|
|
17
|
+
declare const window: any;
|
|
18
|
+
|
|
19
|
+
const _g: any = (function () {
|
|
20
|
+
if (typeof globalThis !== "undefined") return globalThis;
|
|
21
|
+
if (typeof global !== "undefined") return global;
|
|
22
|
+
if (typeof self !== "undefined") return self;
|
|
23
|
+
if (typeof window !== "undefined") return window;
|
|
24
|
+
return {};
|
|
25
|
+
})();
|
|
26
|
+
|
|
27
|
+
if (typeof Symbol === "function" && typeof (Symbol as any).for !== "function") {
|
|
28
|
+
const REGISTRY_KEY = "colyseus.symbolRegistry";
|
|
29
|
+
const registry: { [k: string]: symbol } =
|
|
30
|
+
_g[REGISTRY_KEY] || (_g[REGISTRY_KEY] = Object.create(null));
|
|
31
|
+
(Symbol as any).for = function (key: string): symbol {
|
|
32
|
+
return registry[key] || (registry[key] = Symbol(key));
|
|
33
|
+
};
|
|
34
|
+
(Symbol as any).keyFor = function (sym: symbol): string | undefined {
|
|
35
|
+
for (const k in registry) if (registry[k] === sym) return k;
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const $refId: unique symbol = Symbol.for("$refId");
|
|
2
41
|
export const $track = "~track";
|
|
3
42
|
export const $encoder = "~encoder";
|
|
4
43
|
export const $decoder = "~decoder";
|
|
@@ -13,13 +52,13 @@ export const $deleteByIndex = "~deleteByIndex";
|
|
|
13
52
|
*
|
|
14
53
|
* Real JS Symbol — see the `$values` comment for rationale.
|
|
15
54
|
*/
|
|
16
|
-
export const $changes: unique symbol = Symbol("$changes");
|
|
55
|
+
export const $changes: unique symbol = Symbol.for("$changes");
|
|
17
56
|
|
|
18
57
|
/**
|
|
19
58
|
* Used to keep track of the type of the child elements of a collection
|
|
20
59
|
* (MapSchema, ArraySchema, etc.). Real Symbol — same rationale as $values.
|
|
21
60
|
*/
|
|
22
|
-
export const $childType: unique symbol = Symbol("$childType");
|
|
61
|
+
export const $childType: unique symbol = Symbol.for("$childType");
|
|
23
62
|
|
|
24
63
|
/**
|
|
25
64
|
* Self-reference an instance sets on `this` so its own methods can recover
|
|
@@ -28,7 +67,7 @@ export const $childType: unique symbol = Symbol("$childType");
|
|
|
28
67
|
* once at the top of hot methods and then access fields directly without
|
|
29
68
|
* paying the Proxy.get cost on every read.
|
|
30
69
|
*/
|
|
31
|
-
export const $proxyTarget: unique symbol = Symbol("$proxyTarget");
|
|
70
|
+
export const $proxyTarget: unique symbol = Symbol.for("$proxyTarget");
|
|
32
71
|
|
|
33
72
|
/**
|
|
34
73
|
* Optional "discard" method for custom types (ArraySchema)
|
|
@@ -50,7 +89,7 @@ export const $onDecodeEnd = "~onDecodeEnd";
|
|
|
50
89
|
* which means we can drop Object.defineProperty(...{ enumerable: false })
|
|
51
90
|
* and avoid the slow-path / dictionary-mode hazards that come with it.
|
|
52
91
|
*/
|
|
53
|
-
export const $values: unique symbol = Symbol("$values");
|
|
92
|
+
export const $values: unique symbol = Symbol.for("$values");
|
|
54
93
|
|
|
55
94
|
/**
|
|
56
95
|
* Brand for FieldBuilder instances so schema() can detect them.
|
|
@@ -85,4 +124,4 @@ export const $unreliableFieldIndexes = "~__unreliableFieldIndexes";
|
|
|
85
124
|
export const $transientFieldIndexes = "~__transientFieldIndexes";
|
|
86
125
|
export const $staticFieldIndexes = "~__staticFieldIndexes";
|
|
87
126
|
export const $streamFieldIndexes = "~__streamFieldIndexes";
|
|
88
|
-
export const $streamPriorities = "~__streamPriorities";
|
|
127
|
+
export const $streamPriorities = "~__streamPriorities";
|