@colyseus/schema 5.0.14 → 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.
Files changed (47) hide show
  1. package/README.md +7 -2
  2. package/build/Metadata.d.ts +10 -1
  3. package/build/codegen/api.d.ts +2 -0
  4. package/build/codegen/cli.cjs +322 -31
  5. package/build/codegen/cli.cjs.map +1 -1
  6. package/build/codegen/parser.d.ts +6 -1
  7. package/build/codegen/resolve.d.ts +25 -0
  8. package/build/codegen/types.d.ts +2 -0
  9. package/build/encoder/ChangeTree.d.ts +40 -12
  10. package/build/encoder/Encoder.d.ts +1 -1
  11. package/build/encoder/Root.d.ts +9 -0
  12. package/build/encoder/StateView.d.ts +38 -1
  13. package/build/encoder/changeTree/inheritedFlags.d.ts +13 -19
  14. package/build/encoder/changeTree/liveIteration.d.ts +8 -0
  15. package/build/encoder/changeTree/parentChain.d.ts +30 -8
  16. package/build/encoder/streaming.d.ts +1 -1
  17. package/build/index.cjs +3232 -2830
  18. package/build/index.cjs.map +1 -1
  19. package/build/index.js +3228 -2826
  20. package/build/index.mjs +3232 -2830
  21. package/build/index.mjs.map +1 -1
  22. package/build/types/TypeContext.d.ts +0 -17
  23. package/build/types/builder.d.ts +1 -5
  24. package/build/types/symbols.d.ts +1 -0
  25. package/package.json +1 -1
  26. package/src/Metadata.ts +59 -77
  27. package/src/Reflection.ts +9 -5
  28. package/src/annotations.ts +19 -13
  29. package/src/codegen/api.ts +3 -1
  30. package/src/codegen/cli.ts +5 -2
  31. package/src/codegen/parser.ts +69 -31
  32. package/src/codegen/resolve.ts +322 -0
  33. package/src/codegen/types.ts +4 -1
  34. package/src/decoder/DecodeOperation.ts +13 -2
  35. package/src/encoder/ChangeTree.ts +76 -25
  36. package/src/encoder/EncodeOperation.ts +10 -1
  37. package/src/encoder/Encoder.ts +52 -2
  38. package/src/encoder/Root.ts +28 -8
  39. package/src/encoder/StateView.ts +150 -66
  40. package/src/encoder/changeTree/inheritedFlags.ts +164 -45
  41. package/src/encoder/changeTree/liveIteration.ts +24 -3
  42. package/src/encoder/changeTree/parentChain.ts +72 -15
  43. package/src/encoder/streaming.ts +2 -1
  44. package/src/types/TypeContext.ts +5 -52
  45. package/src/types/builder.ts +14 -10
  46. package/src/types/custom/ArraySchema.ts +57 -14
  47. package/src/types/symbols.ts +3 -0
@@ -1,6 +1,11 @@
1
1
  import * as ts from "typescript";
2
2
  import { Class, Interface, Context, Enum } from "./types.js";
3
- export declare function parseFiles(fileNames: string[], decoratorName?: string, context?: Context): {
3
+ import { ResolveOptions } from "./resolve.js";
4
+ /**
5
+ * `options` is only honored for a top-level call (one passing a fresh
6
+ * `Context`) — the recursive import walk reuses the run's resolver state.
7
+ */
8
+ export declare function parseFiles(fileNames: string[], decoratorName?: string, context?: Context, options?: ResolveOptions): {
4
9
  classes: Class[];
5
10
  interfaces: Interface[];
6
11
  enums: Enum[];
@@ -0,0 +1,25 @@
1
+ export interface ResolveOptions {
2
+ /** Explicit `--tsconfig`. When set, nearest-config discovery is skipped. */
3
+ tsconfig?: string;
4
+ }
5
+ /**
6
+ * Drop every cached tsconfig lookup. Called once per top-level `parseFiles()`
7
+ * run so a long-lived process can generate for two different projects.
8
+ */
9
+ export declare function resetResolver(options?: ResolveOptions): void;
10
+ /**
11
+ * Resolve a non-relative import (`@schemas/Player`, `shared/Player`) to a
12
+ * first-party source file through the tsconfig governing `containingFile`.
13
+ * Returns undefined for npm packages, declaration files, and specifiers no
14
+ * alias covers.
15
+ */
16
+ export declare function resolveNonRelativeImport(specifier: string, containingFile: string): string | undefined;
17
+ /** The extension alternatives parseFiles() probes, in order. Pure — no fs. */
18
+ export declare function sourceFileCandidates(fileName: string): string[];
19
+ /** Same probing as parseFiles(), answering "which candidate exists?". */
20
+ export declare function resolveSourceFile(fileName: string): string | undefined;
21
+ /**
22
+ * The serializer's own source declares wire-internal schemas (`Reflection`,
23
+ * `ReflectionField`, …) that must never reach generated client code.
24
+ */
25
+ export declare function isOwnPackageSource(fileName: string): boolean;
@@ -1,3 +1,5 @@
1
+ /** Root of the @colyseus/schema package — `src/codegen/` in dev, `build/codegen/` once bundled. */
2
+ export declare const PACKAGE_ROOT: string;
1
3
  export declare function getCommentHeader(singleLineComment?: string): string;
2
4
  export declare class Context {
3
5
  classes: Class[];
@@ -54,15 +54,30 @@ export interface ChangeTreeList {
54
54
  nextPosition: number;
55
55
  }
56
56
  export declare function createChangeTreeList(): ChangeTreeList;
57
+ /**
58
+ * Live node in a tree's parent chain — mutating one edits the chain. Only
59
+ * `parentChain.ts` should hold these.
60
+ */
57
61
  export interface ParentChain {
58
62
  ref: Ref;
59
63
  index: number;
60
64
  next?: ParentChain;
61
65
  }
66
+ /**
67
+ * Detached copy of one parent link, handed out by the query helpers. Distinct
68
+ * from `ParentChain` on purpose: it carries no `next`, so it cannot be walked
69
+ * as if it were the chain, and it is readonly, so it cannot be mistaken for a
70
+ * way to move a parent's index — `setParentIndex` does that.
71
+ */
72
+ export interface ParentEntry {
73
+ readonly ref: Ref;
74
+ readonly index: number;
75
+ }
62
76
  export declare const IS_FILTERED = 1, IS_VISIBILITY_SHARED = 2, IS_NEW = 4;
63
77
  export declare const IS_UNRELIABLE = 8, IS_PATCH_ONLY = 16, IS_FULL_STATE_ONLY = 32;
64
78
  export declare const IS_STREAM_COLLECTION = 64;
65
79
  export declare const NEEDS_RESTAGE = 128;
80
+ export declare const PENDING_FILTER_REFRESH = 256;
66
81
  /**
67
82
  * Flags a child inherits from its parent's own transitive state via
68
83
  * `checkInheritedFlags`. Read as a bitwise mask so the inheritance step
@@ -94,6 +109,13 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
94
109
  * facing identity (debug output, callback parents) keep using `ref`.
95
110
  */
96
111
  refTarget: T;
112
+ /**
113
+ * True when `ref` is an ArraySchema — the only proxied type, so its
114
+ * user-facing identity differs from `refTarget`. Canonical predicate for
115
+ * "is this tree's ref an array" without probing `ref` (which would hit
116
+ * the Proxy trap) — two monomorphic loads on the tree itself.
117
+ */
118
+ get isArray(): boolean;
97
119
  metadata: Metadata;
98
120
  /**
99
121
  * Per-class cache of encoder fn / filter fn / isSchema / metadata /
@@ -162,7 +184,7 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
162
184
  isFieldUnreliable(index: number): boolean;
163
185
  isFieldFullStateOnly(index: number): boolean;
164
186
  isFieldStream(index: number): boolean;
165
- constructor(ref: T);
187
+ constructor(ref: T, refTarget?: T);
166
188
  private _opAt;
167
189
  private _opPut;
168
190
  private _markDirty;
@@ -190,14 +212,21 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
190
212
  */
191
213
  recycle(): void;
192
214
  /**
193
- * ArraySchema#unshift(): re-key pending ops on both channels by
194
- * `+count`, then record ADDs for the new items at indexes 0..count-1.
215
+ * ArraySchema insert (unshift / splice with more inserts than deletes):
216
+ * re-key pending ops at or above `at` by `+count`, then record ADDs for
217
+ * the new items at indexes `at..at+count-1`.
195
218
  *
196
- * The rebuilt map's insertion order IS the wire order: new ADDs first
197
- * (ascending — the decoder splice-inserts each one, which only works
198
- * lowest-index-first), then prior ops in their original relative order
199
- * at their shifted positions. See ArraySchema#$setAt.
219
+ * The rebuilt map's insertion order IS the wire order:
220
+ * 1. ops below `at` — the insert doesn't move them, and an insert of
221
+ * their own must still be applied before this one (ascending);
222
+ * 2. the new ADDs, ascending the decoder splice-inserts each one,
223
+ * which only works lowest-index-first;
224
+ * 3. the re-keyed ops, in their original relative order — their
225
+ * indexes now address the post-insert layout.
226
+ * See ArraySchema#$setAt.
200
227
  */
228
+ insertAt(at: number, count: number): void;
229
+ /** ArraySchema#unshift(): insert `count` items at the head. */
201
230
  unshift(count: number): void;
202
231
  setRoot(root: Root): void;
203
232
  setParent(parent: Ref, root?: Root, parentIndex?: number): void;
@@ -255,12 +284,11 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
255
284
  setParentIndex(parent: Ref, index: number): void;
256
285
  /** @returns true if parent was found and removed */
257
286
  removeParent(parent?: Ref): boolean;
258
- findParent(predicate: (parent: Ref, index: number) => boolean): ParentChain | undefined;
287
+ findParent(predicate: (parent: Ref, index: number) => boolean): ParentEntry | undefined;
259
288
  hasParent(predicate: (parent: Ref, index: number) => boolean): boolean;
260
- getAllParents(): Array<{
261
- ref: Ref;
262
- index: number;
263
- }>;
289
+ /** Wire index this tree holds inside `parent`, or undefined if not a parent. */
290
+ indexInParent(parent: Ref): number | undefined;
291
+ getAllParents(): ParentEntry[];
264
292
  }
265
293
  /**
266
294
  * Lightweight per-instance no-op ChangeTree used for instances the decoder
@@ -2,7 +2,7 @@ import type { Schema } from "../Schema.js";
2
2
  import { TypeContext } from "../types/TypeContext.js";
3
3
  import type { Iterator } from "../encoding/decode.js";
4
4
  import { Root } from "./Root.js";
5
- import type { StateView } from "./StateView.js";
5
+ import { type StateView } from "./StateView.js";
6
6
  export declare class Encoder<T extends Schema = any> {
7
7
  /**
8
8
  * Per-encoder shared output buffer size. The encoder auto-grows on
@@ -46,6 +46,15 @@ export declare class Root {
46
46
  * fields dirty at the same time.
47
47
  */
48
48
  unreliableChanges: ChangeTreeList;
49
+ /**
50
+ * Trees whose parent-edge set changed this tick (instance sharing
51
+ * gained or lost an edge). The encoder drains this before emission —
52
+ * `inheritedFlags.drainFilterRefresh` re-derives each tree's filter
53
+ * state against the then-settled containers. Only populated when the
54
+ * TypeContext has any @view/@stream field.
55
+ */
56
+ pendingFilterRefresh: ChangeTree[];
57
+ enqueueFilterRefresh(tree: ChangeTree): void;
49
58
  /**
50
59
  * Free-list of ChangeTreeNode objects. Both queues share this pool —
51
60
  * a node carries no queue affinity, only `{ changeTree, prev, next, position }`.
@@ -5,6 +5,21 @@ import type { MapSchema } from "../types/custom/MapSchema.js";
5
5
  import type { SetSchema } from "../types/custom/SetSchema.js";
6
6
  import type { CollectionSchema } from "../types/custom/CollectionSchema.js";
7
7
  export declare function createView(iterable?: boolean): StateView;
8
+ /**
9
+ * Compact description of a rejected argument, for warning messages.
10
+ * Passing the value itself to `console.warn` is not an option — a
11
+ * populated collection inspects into dozens of lines of encoder
12
+ * internals and buries the message that matters.
13
+ */
14
+ /**
15
+ * Sentinel inner-map key: "snapshot every live element of this ref-typed
16
+ * ArraySchema". Written by `_add`'s bulk path instead of one entry per
17
+ * element; `encodeView` expands it structurally at drain time, so the
18
+ * emitted slots reflect any reindex that happened after `view.add()` —
19
+ * and a whole-array snapshot costs one Map insert instead of N.
20
+ * Real slots are never negative, so -1 cannot collide.
21
+ */
22
+ export declare const ARRAY_SNAPSHOT = -1;
8
23
  export declare class StateView {
9
24
  iterable: boolean;
10
25
  /**
@@ -36,8 +51,16 @@ export declare class StateView {
36
51
  * Inner storage is a Map so the encode loop in `encodeView` can iterate
37
52
  * directly with numeric keys — the legacy `{[index]: OPERATION}` shape
38
53
  * forced an `Object.keys(...)` allocation + `Number(key)` parse per ref.
54
+ *
55
+ * Inner keys are numbers (Schema field indexes, MapSchema journal
56
+ * indexes, Set/Collection indexes, stream positions — all stable within
57
+ * a tick), EXCEPT element bindings under a ref-typed ArraySchema parent,
58
+ * which are keyed by the child's ChangeTree. An array wire slot captured
59
+ * at `view.add()` time goes stale if the array reindexes (unshift /
60
+ * reverse / move) later in the same tick — identity keys let
61
+ * `encodeView` resolve the CURRENT slot at drain time instead.
39
62
  */
40
- changes: Map<number, Map<number, OPERATION>>;
63
+ changes: Map<number, Map<number | ChangeTree<any>, OPERATION>>;
41
64
  constructor(iterable?: boolean);
42
65
  /**
43
66
  * Lazily bind this view to a Root and acquire a view ID. Called on
@@ -183,4 +206,18 @@ export declare class StateView {
183
206
  clear(): void;
184
207
  isChangeTreeVisible(changeTree: ChangeTree): boolean;
185
208
  protected _recursiveDeleteVisibleChangeTree(changeTree: ChangeTree): void;
209
+ /**
210
+ * Drop the pending `view.changes` entries of `tree` and every descendant.
211
+ * Called when a same-patch pending ADD is cancelled: the subtree's
212
+ * introduction never reaches this client, so its entries would emit
213
+ * refIds the decoder cannot resolve ("refId" not found).
214
+ */
215
+ private _dropPendingEntries;
216
+ /**
217
+ * Queue DELETE for a @view field on `changes` and hide the field
218
+ * value's subtree from this view. When the field's ADD is still
219
+ * pending (same-patch add + remove), the value's introduction never
220
+ * ships — its pending subtree entries are dropped along with it.
221
+ */
222
+ private _removeViewField;
186
223
  }
@@ -1,4 +1,5 @@
1
1
  import { type ChangeTree, type Ref } from "../ChangeTree.js";
2
+ import type { Root } from "../Root.js";
2
3
  /**
3
4
  * Reconcile queue membership + inherited flags for a tree that just had
4
5
  * its root/parent assigned. See `_checkInheritedFlags` for the flag
@@ -11,24 +12,17 @@ export declare function checkIsFiltered(tree: ChangeTree, parent: Ref, parentInd
11
12
  * etc.) inherit these from the Schema field that holds them.
12
13
  *
13
14
  * The common case — fresh tree attached to a parent field that carries
14
- * none of the inheritable annotations — produces no flag change, no
15
- * queue update, and no `parentFiltered` hit. Two small structural
16
- * choices keep that case cheap without any precomputed descriptor
17
- * bitmask:
18
- *
19
- * 1) Flag inheritance is a single bitwise OR onto `tree.flags`. The
20
- * three per-annotation reads pack into `fieldBits`, the parent's
21
- * inherited bits come from `parentChangeTree.flags` directly; one
22
- * read-modify-write replaces three getter/setter cycles, and the
23
- * bit diff against `beforeFlags` gives us the "just became static /
24
- * unreliable" signal for the side-effect branches.
25
- *
26
- * 2) The `parentFiltered` string-key lookup is gated on
27
- * `types.hasParentFilteredEntries`, which is only flipped true when
28
- * `registerFilteredByParent` actually records an entry — i.e. when
29
- * some @view-tagged field reaches this (child, parent, index)
30
- * triple through the ancestry walk. Schemas with @view tags only on
31
- * sibling fields (not along any attachment chain) skip the string
32
- * concat + hash lookup entirely.
15
+ * none of the inheritable annotations — produces no flag change and no
16
+ * queue update. Flag inheritance is a single bitwise OR onto
17
+ * `tree.flags`: the per-annotation reads pack into `fieldBits`, the
18
+ * parent's inherited bits come from `parentChangeTree.flags` directly,
19
+ * and one read-modify-write replaces three getter/setter cycles. The bit
20
+ * diff against `beforeFlags` gives the "just became static / unreliable"
21
+ * signal for the side-effect branches.
33
22
  */
34
23
  export declare function checkInheritedFlags(tree: ChangeTree, parent: Ref, parentIndex: number): void;
24
+ /**
25
+ * Drain `root.pendingFilterRefresh`. Called by the encoder before any
26
+ * emission (per-tick channels and full-sync).
27
+ */
28
+ export declare function drainFilterRefresh(root: Root): void;
@@ -1,3 +1,11 @@
1
1
  import type { ChangeTree } from "../ChangeTree.js";
2
+ /**
3
+ * Re-stage one live index as a fresh ADD on its channel. Shared by
4
+ * `Root.add` (refCount-0 / NEEDS_RESTAGE re-adds) and
5
+ * `inheritedFlags.refreshFilterState` (filtered→public flip) via
6
+ * `forEachLiveWithCtx(tree, restageLiveCb)` — one home for the
7
+ * unreliable-routing rule.
8
+ */
9
+ export declare const restageLiveCb: (tree: ChangeTree, fieldIndex: number) => void;
2
10
  export declare function forEachLive(tree: ChangeTree, callback: (index: number) => void): void;
3
11
  export declare function forEachLiveWithCtx<C>(tree: ChangeTree, ctx: C, cb: (ctx: C, index: number) => void): void;
@@ -1,4 +1,4 @@
1
- import type { ChangeTree, ParentChain, Ref } from "../ChangeTree.js";
1
+ import type { ChangeTree, ParentEntry, Ref } from "../ChangeTree.js";
2
2
  /**
3
3
  * Add a parent to the chain. If `parent` already exists anywhere in the
4
4
  * chain, update the primary parent's index instead (matches legacy
@@ -20,14 +20,36 @@ export declare function setParentIndex(tree: ChangeTree, parent: Ref, index: num
20
20
  */
21
21
  export declare function removeParent(tree: ChangeTree, parent: Ref): boolean;
22
22
  /**
23
- * Find the first parent in the chain matching `predicate`.
23
+ * First parent matching `predicate`, as a detached `ParentEntry`. Never returns
24
+ * a live `ParentChain` node — the inline parent has no node to return in the
25
+ * first place, so handing out the real node for the `extraParents` case only
26
+ * would make writes land or vanish depending on which parent matched. Use
27
+ * `setParentIndex` to move an index and `indexInParent` to read one.
24
28
  */
25
- export declare function findParent(tree: ChangeTree, predicate: (parent: Ref, index: number) => boolean): ParentChain | undefined;
29
+ export declare function findParent(tree: ChangeTree, predicate: (parent: Ref, index: number) => boolean): ParentEntry | undefined;
30
+ /** Walks in place — `addParent` calls this per shared-instance attach. */
26
31
  export declare function hasParent(tree: ChangeTree, predicate: (parent: Ref, index: number) => boolean): boolean;
27
32
  /**
28
- * Return all parents as an array (debug/test helper).
33
+ * Wire index `tree` holds inside `parent`, or undefined when `parent` is
34
+ * nowhere in the chain. Allocation-free variant of `findParent` for the
35
+ * encodeView drain, which resolves identity-keyed view entries per emission.
36
+ *
37
+ * A child detached from `parent` this tick usually still resolves: Root.remove
38
+ * leaves the child's own parent link dangling, and the staged snapshot keeps
39
+ * the child in `tmpItems` (so reindexes keep the index current) until
40
+ * `$onEncodeEnd` — which runs after the drain.
41
+ */
42
+ export declare function indexInParent(tree: ChangeTree, parent: Ref): number | undefined;
43
+ /**
44
+ * Return all parents as detached entries (debug/test helper).
45
+ */
46
+ export declare function getAllParents(tree: ChangeTree): ParentEntry[];
47
+ /**
48
+ * True iff `parent` currently holds `tree`. Detached edges linger in the
49
+ * parent chain (load-bearing for same-tick view drains — see
50
+ * `indexInParent` above), so the chain alone cannot answer which edges
51
+ * are live. ArraySchema is probed by scanning `items`: the recorded slot
52
+ * can go stale after reorders, and `items` — unlike `$getByIndex`'s staged
53
+ * view — reflects the tick's completed mutations.
29
54
  */
30
- export declare function getAllParents(tree: ChangeTree): Array<{
31
- ref: Ref;
32
- index: number;
33
- }>;
55
+ export declare function isEdgeLive(tree: ChangeTree, parentTree: ChangeTree, index: number): boolean;
@@ -72,7 +72,7 @@ export declare function streamEnqueueForView(s: Streamable, viewId: number, inde
72
72
  * `view.changes`; false if it was only pending (silent drop) or not
73
73
  * present at all.
74
74
  */
75
- export declare function streamDequeueForView(s: Streamable, viewId: number, refId: number, index: number, viewChanges: Map<number, Map<number, number>>): boolean;
75
+ export declare function streamDequeueForView(s: Streamable, viewId: number, refId: number, index: number, viewChanges: Map<number, Map<number | object, number>>): boolean;
76
76
  /**
77
77
  * Drop all per-view state for a disposing/GC'd StateView. Keeps memory
78
78
  * bounded in long-running rooms with client churn.