@colyseus/schema 5.0.13 → 5.0.19
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 +7 -2
- package/build/Metadata.d.ts +10 -1
- package/build/Reflection.d.ts +36 -34
- package/build/codegen/api.d.ts +2 -0
- package/build/codegen/cli.cjs +322 -31
- package/build/codegen/cli.cjs.map +1 -1
- package/build/codegen/parser.d.ts +6 -1
- package/build/codegen/resolve.d.ts +25 -0
- package/build/codegen/types.d.ts +2 -0
- package/build/encoder/ChangeTree.d.ts +40 -12
- package/build/encoder/Encoder.d.ts +1 -1
- package/build/encoder/Root.d.ts +9 -0
- package/build/encoder/StateView.d.ts +38 -1
- package/build/encoder/changeTree/inheritedFlags.d.ts +13 -19
- package/build/encoder/changeTree/liveIteration.d.ts +8 -0
- package/build/encoder/changeTree/parentChain.d.ts +30 -8
- package/build/encoder/streaming.d.ts +1 -1
- package/build/index.cjs +3237 -2834
- package/build/index.cjs.map +1 -1
- package/build/index.js +3233 -2830
- package/build/index.mjs +3237 -2834
- package/build/index.mjs.map +1 -1
- package/build/types/HelperTypes.d.ts +3 -5
- package/build/types/TypeContext.d.ts +0 -17
- package/build/types/builder.d.ts +8 -11
- package/build/types/symbols.d.ts +1 -0
- package/package.json +2 -2
- package/src/Metadata.ts +59 -77
- package/src/Reflection.ts +9 -5
- package/src/annotations.ts +19 -13
- package/src/codegen/api.ts +3 -1
- package/src/codegen/cli.ts +5 -2
- package/src/codegen/parser.ts +69 -31
- package/src/codegen/resolve.ts +322 -0
- package/src/codegen/types.ts +4 -1
- package/src/decoder/DecodeOperation.ts +13 -2
- package/src/encoder/ChangeTree.ts +76 -25
- package/src/encoder/EncodeOperation.ts +10 -1
- package/src/encoder/Encoder.ts +52 -2
- package/src/encoder/Root.ts +28 -8
- package/src/encoder/StateView.ts +150 -66
- package/src/encoder/changeTree/inheritedFlags.ts +164 -45
- package/src/encoder/changeTree/liveIteration.ts +24 -3
- package/src/encoder/changeTree/parentChain.ts +72 -15
- package/src/encoder/streaming.ts +2 -1
- package/src/types/HelperTypes.ts +16 -13
- package/src/types/TypeContext.ts +5 -52
- package/src/types/builder.ts +21 -16
- package/src/types/custom/ArraySchema.ts +57 -14
- package/src/types/symbols.ts +3 -0
package/src/types/builder.ts
CHANGED
|
@@ -51,10 +51,11 @@ export type BuilderOf<T> = FieldBuilder<T>;
|
|
|
51
51
|
* Schema ref whose `initialize` takes zero args.
|
|
52
52
|
* - `IsOptional` is a compile-time brand for `.optional()`. Both
|
|
53
53
|
* `HasDefault` and `IsOptional` make the field omittable in
|
|
54
|
-
* `BuilderInitProps<T
|
|
55
|
-
* `undefined extends V`)
|
|
56
|
-
*
|
|
57
|
-
*
|
|
54
|
+
* `BuilderInitProps<T>`; `IsOptional` alone marks the instance property
|
|
55
|
+
* `?:`. A separate brand (rather than reading `undefined extends V`)
|
|
56
|
+
* keeps both correct for consumers compiling with
|
|
57
|
+
* `strictNullChecks: false`, where `undefined extends V` is true for
|
|
58
|
+
* every V.
|
|
58
59
|
*
|
|
59
60
|
* schema() reads the internal configuration via `toDefinition()` and wires
|
|
60
61
|
* up metadata through the existing pipeline.
|
|
@@ -289,8 +290,8 @@ export function isBuilder(value: any): value is FieldBuilder<any> {
|
|
|
289
290
|
* Two call signatures, NOT a defaulted generic `<T extends TBase = TBase>`: the
|
|
290
291
|
* bare form must return a CONCRETE `FieldBuilder<TBase>` so `schema({ x:
|
|
291
292
|
* t.number() })` still infers `x: number`. A defaulted free type parameter gets
|
|
292
|
-
* captured as `any` during `schema()`'s self-referential field inference
|
|
293
|
-
*
|
|
293
|
+
* captured as `any` during `schema()`'s self-referential field inference,
|
|
294
|
+
* degrading every field's value type to `any`.
|
|
294
295
|
*
|
|
295
296
|
* NOTE: the refinement is a TYPE-LEVEL assertion, not a runtime guarantee — the
|
|
296
297
|
* wire still carries the codec's full range and the DECODER writes whatever
|
|
@@ -306,16 +307,24 @@ function primitive<TBase>(name: RawPrimitiveType): PrimitiveFactory<TBase> {
|
|
|
306
307
|
return (() => new FieldBuilder<TBase>(name)) as PrimitiveFactory<TBase>;
|
|
307
308
|
}
|
|
308
309
|
|
|
309
|
-
//
|
|
310
|
+
// Collection element: a Schema class or a primitive type NAME (`"string"`).
|
|
310
311
|
export type ChildType =
|
|
311
312
|
| RawPrimitiveType
|
|
312
|
-
| Constructor<Schema
|
|
313
|
-
| FieldBuilder<any>;
|
|
313
|
+
| Constructor<Schema>;
|
|
314
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Guard against `t.array(t.string())`. A builder child looks like it should
|
|
317
|
+
* work — and its bare `_type` would — but every modifier on it (`.view()`,
|
|
318
|
+
* `.default()`, quantize options) would be silently dropped, since modifiers
|
|
319
|
+
* describe the FIELD, not the elements. Fail loudly instead.
|
|
320
|
+
*/
|
|
315
321
|
function resolveChild(child: ChildType): DefinitionType {
|
|
316
322
|
if (isBuilder(child)) {
|
|
317
|
-
|
|
318
|
-
|
|
323
|
+
const inner = child['_type']; // private; element access bypasses the check
|
|
324
|
+
const hint = (typeof inner === "string")
|
|
325
|
+
? `use the type name instead: t.array("${inner}")`
|
|
326
|
+
: `collections accept a Schema class or a primitive type name ("string", "number", …)`;
|
|
327
|
+
throw new Error(`t.array/map/set/collection(): a t.* builder is not a valid element type — ${hint}.`);
|
|
319
328
|
}
|
|
320
329
|
return child as DefinitionType;
|
|
321
330
|
}
|
|
@@ -325,28 +334,24 @@ function resolveChild(child: ChildType): DefinitionType {
|
|
|
325
334
|
// ---------------------------------------------------------------------------
|
|
326
335
|
|
|
327
336
|
// Overloaded factories for collections. Implementation lives in a single function;
|
|
328
|
-
// overloads narrow the return type for Schema/primitive
|
|
337
|
+
// overloads narrow the return type for Schema/primitive children.
|
|
329
338
|
// All collection factories tag `HasDefault = true` because schema() auto-
|
|
330
339
|
// instantiates an empty collection when no explicit default is given.
|
|
331
340
|
interface ArrayFactory {
|
|
332
341
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<ArraySchema<InstanceType<C>>, true, false>;
|
|
333
342
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<ArraySchema<InferValueType<P>>, true, false>;
|
|
334
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<ArraySchema<V>, true, false>;
|
|
335
343
|
}
|
|
336
344
|
interface MapFactory {
|
|
337
345
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<MapSchema<InstanceType<C>>, true, false>;
|
|
338
346
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<MapSchema<InferValueType<P>>, true, false>;
|
|
339
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<MapSchema<V>, true, false>;
|
|
340
347
|
}
|
|
341
348
|
interface SetFactory {
|
|
342
349
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<SetSchema<InstanceType<C>>, true, false>;
|
|
343
350
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<SetSchema<InferValueType<P>>, true, false>;
|
|
344
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<SetSchema<V>, true, false>;
|
|
345
351
|
}
|
|
346
352
|
interface CollectionFactory {
|
|
347
353
|
<C extends Constructor<Schema>>(child: C): FieldBuilder<CollectionSchema<InstanceType<C>>, true, false>;
|
|
348
354
|
<P extends RawPrimitiveType>(child: P): FieldBuilder<CollectionSchema<InferValueType<P>>, true, false>;
|
|
349
|
-
<V>(child: FieldBuilder<V>): FieldBuilder<CollectionSchema<V>, true, false>;
|
|
350
355
|
}
|
|
351
356
|
// t.stream(Entity) — priority-batched collection of Schema instances.
|
|
352
357
|
// Element type is restricted to Schema subclasses (no primitives) because
|
|
@@ -178,7 +178,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
178
178
|
const proxy = new Proxy(this, ARRAY_PROXY_HANDLER);
|
|
179
179
|
|
|
180
180
|
Object.defineProperty(this, $changes, {
|
|
181
|
-
value: new ChangeTree(proxy),
|
|
181
|
+
value: new ChangeTree(proxy, this),
|
|
182
182
|
enumerable: false,
|
|
183
183
|
writable: true,
|
|
184
184
|
});
|
|
@@ -486,7 +486,22 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
486
486
|
// @ts-ignore
|
|
487
487
|
reverse(): ArraySchema<V> {
|
|
488
488
|
const self = this[$proxyTarget];
|
|
489
|
-
self[$changes]
|
|
489
|
+
const changeTree = self[$changes];
|
|
490
|
+
|
|
491
|
+
if (changeTree.has() || self.deletedIndexes.length > 0) {
|
|
492
|
+
//
|
|
493
|
+
// Ops recorded earlier this tick address the staged (pre-reverse)
|
|
494
|
+
// layout, and the encoder only resolves their values at encode
|
|
495
|
+
// time — a pure REVERSE would move that layout under them.
|
|
496
|
+
// Degrade to a full re-state: CLEAR + re-ADD in reversed order.
|
|
497
|
+
//
|
|
498
|
+
const reversed = self.items.slice().reverse();
|
|
499
|
+
this.clear(); // also drops staged holes (discard → $onEncodeEnd)
|
|
500
|
+
this.push(...reversed);
|
|
501
|
+
return this;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
changeTree.operation(OPERATION.REVERSE);
|
|
490
505
|
self.items.reverse();
|
|
491
506
|
self.tmpItems.reverse();
|
|
492
507
|
self.$reindexChildren(0);
|
|
@@ -602,13 +617,13 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
602
617
|
|
|
603
618
|
// insert operations
|
|
604
619
|
if (insertCount > 0) {
|
|
605
|
-
|
|
606
|
-
console.error("Inserting more elements than deleting during ArraySchema#splice()");
|
|
607
|
-
throw new Error("ArraySchema#splice(): insertCount must be equal or lower than deleteCount.");
|
|
608
|
-
}
|
|
620
|
+
const base = indexes[start] ?? itemsLength;
|
|
609
621
|
|
|
610
|
-
|
|
611
|
-
|
|
622
|
+
// the first `reuse` items take over the wire slots just deleted
|
|
623
|
+
const reuse = Math.min(insertCount, deleteCount);
|
|
624
|
+
|
|
625
|
+
for (let i = 0; i < reuse; i++) {
|
|
626
|
+
const addIndex = base + i;
|
|
612
627
|
|
|
613
628
|
changeTree.indexedOperation(
|
|
614
629
|
addIndex,
|
|
@@ -617,9 +632,36 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
617
632
|
: OPERATION.ADD
|
|
618
633
|
);
|
|
619
634
|
|
|
635
|
+
// the slot is live again — the staged snapshot must carry the
|
|
636
|
+
// new value, or `$getByIndex` falls back to `items[addIndex]`
|
|
637
|
+
// and resolves an unrelated element once tmp/items diverge.
|
|
638
|
+
tmpItems[addIndex] = insertItems[i];
|
|
639
|
+
deletedIndexes[addIndex] = false;
|
|
640
|
+
|
|
620
641
|
// set value's parent/root — use `this` (Proxy) as parent.
|
|
621
642
|
insertItems[i][$changes]?.setParent(this, changeTree.root, addIndex);
|
|
622
643
|
}
|
|
644
|
+
|
|
645
|
+
// ...the rest have no slot to take: widen the wire layout, same as
|
|
646
|
+
// unshift() but at `at` instead of 0.
|
|
647
|
+
const extra = insertCount - reuse;
|
|
648
|
+
if (extra > 0) {
|
|
649
|
+
const at = base + reuse;
|
|
650
|
+
|
|
651
|
+
changeTree.insertAt(at, extra);
|
|
652
|
+
|
|
653
|
+
for (let i = 0; i < extra; i++) {
|
|
654
|
+
insertItems[reuse + i][$changes]?.setParent(this, changeTree.root, at + i);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// keep staged-delete flags aligned with the inserted tmp slots
|
|
658
|
+
if (deletedIndexes.length > 0) {
|
|
659
|
+
deletedIndexes.splice(at, 0, ...new Array(extra).fill(false));
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
tmpItems.splice(at, 0, ...insertItems.slice(reuse));
|
|
663
|
+
self.$reindexChildren(at + extra); // survivors only — the loop above placed the new items
|
|
664
|
+
}
|
|
623
665
|
}
|
|
624
666
|
|
|
625
667
|
changeTree.root?.enqueueChangeTree(changeTree);
|
|
@@ -969,15 +1011,16 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
969
1011
|
}
|
|
970
1012
|
|
|
971
1013
|
protected [$onEncodeEnd]() {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1014
|
+
// No unwrap: ChangeTree's gated sites are the only callers and they
|
|
1015
|
+
// invoke on `refTarget` (the raw target) already.
|
|
1016
|
+
const staged = this.tmpItems;
|
|
1017
|
+
this.tmpItems = this.items.slice();
|
|
975
1018
|
|
|
976
|
-
if (
|
|
1019
|
+
if (this.deletedIndexes.length > 0) {
|
|
977
1020
|
// compaction just closed the staged holes — everything above the
|
|
978
1021
|
// lowest one slid down a slot
|
|
979
|
-
|
|
980
|
-
|
|
1022
|
+
this.$reindexChildren(0, staged);
|
|
1023
|
+
this.deletedIndexes.length = 0;
|
|
981
1024
|
}
|
|
982
1025
|
}
|
|
983
1026
|
|
package/src/types/symbols.ts
CHANGED
|
@@ -131,6 +131,9 @@ export const $viewFieldIndexes = "~__viewFieldIndexes";
|
|
|
131
131
|
export const $fieldIndexesByViewTag = "$__fieldIndexesByViewTag";
|
|
132
132
|
export const $unreliableFieldIndexes = "~__unreliableFieldIndexes";
|
|
133
133
|
export const $patchOnlyFieldIndexes = "~__patchOnlyFieldIndexes";
|
|
134
|
+
// @patchOnly ∪ @deprecated() — indexes the full-sync walk must not read (the
|
|
135
|
+
// deprecated accessor may throw). Maintained at decoration time.
|
|
136
|
+
export const $fullSyncSkipIndexes = "~__fullSyncSkipIndexes";
|
|
134
137
|
export const $fullStateOnlyFieldIndexes = "~__fullStateOnlyFieldIndexes";
|
|
135
138
|
export const $streamFieldIndexes = "~__streamFieldIndexes";
|
|
136
139
|
export const $streamPriorities = "~__streamPriorities";
|