@colyseus/schema 5.0.24 → 5.0.25

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.
@@ -136,9 +136,11 @@ export type IsNever<T> = [T] extends [never] ? true : false;
136
136
  * - Primitives can be assigned directly
137
137
  * - Schema instances can be assigned from plain objects or Schema instances
138
138
  * - Collections can be assigned from their JSON representations
139
+ *
140
+ * Keys filter through `DataKey` in the `as` clause — see THE RULE above.
139
141
  */
140
142
  export type AssignableProps<T> = {
141
- [K in NonFunctionPropNames<T>]?: AssignableValue<T[K]>;
143
+ -readonly [K in keyof T as DataKey<T, K>]?: AssignableValue<T[K]>;
142
144
  };
143
145
  /**
144
146
  * Value-level assignment shape shared by `AssignableProps` and
@@ -153,20 +155,18 @@ export type RefHasDefault<C> = C extends {
153
155
  } ? (P extends readonly [] ? true : false) : true;
154
156
  type FieldValue<F> = F extends FieldBuilder<infer V, boolean, boolean> ? V : F extends new (...args: any[]) => infer I ? (I extends Schema ? I : never) : never;
155
157
  type KeyClass<T, K extends keyof T> = T[K] extends FieldBuilder<unknown, infer D extends boolean, infer O extends boolean> ? (D extends true ? "optional" : O extends true ? "optional" : "required") : T[K] extends new (...args: any[]) => Schema ? (RefHasDefault<T[K]> extends true ? "optional" : "required") : "none";
156
- export type BuilderRequiredKeys<T> = {
157
- [K in keyof T]-?: KeyClass<T, K> extends "required" ? K : never;
158
- }[keyof T];
159
- export type BuilderOptionalKeys<T> = {
160
- [K in keyof T]-?: KeyClass<T, K> extends "optional" ? K : never;
161
- }[keyof T];
162
158
  /**
163
159
  * Constructor/init-props type for a schema() fields map. Required fields
164
160
  * (primitives without `.default()` or `.optional()`, and Schema refs with
165
161
  * non-zero-arg `initialize()`) are `:`; everything else is `?:`.
162
+ *
163
+ * Split by `KeyClass` in the `as` clause — see THE RULE above. `-?` matters
164
+ * under `strictNullChecks: false`: an optional key in the fields map still
165
+ * classifies as "required" there, and would otherwise inherit the `?`.
166
166
  */
167
167
  export type BuilderInitProps<T> = {
168
- [K in BuilderRequiredKeys<T>]: AssignableValue<FieldValue<T[K]>>;
168
+ -readonly [K in keyof T as KeyClass<T, K> extends "required" ? K : never]-?: AssignableValue<FieldValue<T[K]>>;
169
169
  } & {
170
- [K in BuilderOptionalKeys<T>]?: AssignableValue<Exclude<FieldValue<T[K]>, undefined>>;
170
+ -readonly [K in keyof T as KeyClass<T, K> extends "optional" ? K : never]?: AssignableValue<Exclude<FieldValue<T[K]>, undefined>>;
171
171
  };
172
172
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/schema",
3
- "version": "5.0.24",
3
+ "version": "5.0.25",
4
4
  "description": "Automatic state replication for multiplayer games. Mutate plain objects, and every client gets a live, typed mirror through delta encoding.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -619,7 +619,18 @@ export class StateView {
619
619
  this.changes.set(changeTree.ref[$refId], changes);
620
620
  }
621
621
 
622
- this.addTag(changeTree, tag);
622
+ // Grant the tag on the parent only when the field pointing at
623
+ // the child carries an overlapping tag — that field is the sole
624
+ // reason the parent needs it. Granting unconditionally handed
625
+ // the view every OTHER same-tag field on the parent, which then
626
+ // rode out on its next mutation (never at add() time, so it read
627
+ // as "the field only shows up after it changes").
628
+ const parentFieldTag = changeTree.encDescriptor.tags[parentIndex];
629
+ if (parentFieldTag !== undefined &&
630
+ parentFieldTag !== DEFAULT_VIEW_TAG &&
631
+ (parentFieldTag & tag) !== 0) {
632
+ this.addTag(changeTree, tag);
633
+ }
623
634
 
624
635
  // ArraySchema parents: key by the child's identity, not the wire
625
636
  // slot it holds right now — a same-tick unshift()/reverse()/move()
@@ -117,11 +117,12 @@ export type CodecFor<T> = {
117
117
  type IsOptionalBuilderKey<T, K extends keyof T> =
118
118
  T[K] extends FieldBuilder<unknown, boolean, infer O extends boolean> ? O : false;
119
119
 
120
- // Split in the `as` clause, never over a precomputed key union: only a
121
- // homomorphic mapping carries each key back to its declaration in the
122
- // `schema({ })` literal, which is what go-to-definition and rename resolve
123
- // through. `-readonly`, and `-?` on the required half, drop the modifiers a
124
- // homomorphic mapping inherits from the fields map.
120
+ // THE RULE for every mapped type below that projects a user's declared fields:
121
+ // split in the `as` clause, never over a precomputed key union. Only a
122
+ // homomorphic mapping carries each key back to its declaration, and that link
123
+ // is what go-to-definition and rename resolve through losing it leaves rename
124
+ // silently touching just the cursor (colyseus/colyseus#958). The `-readonly`
125
+ // and `-?` that follow drop the modifiers such a mapping inherits from `T`.
125
126
  export type InferSchemaInstanceType<T> = {
126
127
  -readonly [K in keyof T as IsOptionalBuilderKey<T, K> extends true ? never : K]-?: T[K] extends FieldBuilder<any>
127
128
  ? InferValueType<T[K]>
@@ -170,12 +171,11 @@ type IsOptionalKey<T, K extends keyof T> = undefined extends {}
170
171
  ? ({} extends Pick<T, K> ? true : false)
171
172
  : (undefined extends T[K] ? true : false);
172
173
 
173
- // Both filters live in the `as` clause, for two independent reasons:
174
- // navigation, as above and depth, because the clause runs before the value
175
- // type, so `ToJSONField` never reaches the machinery, where it would recurse
176
- // through `restore(json: ToJSON<this>)` past TypeScript 7's instantiation limit.
177
- // Probing `IsOptionalKey` first is deliberate: `DataKey` then runs in the taken
178
- // branch only, once per key rather than once per half.
174
+ // Beyond THE RULE, the `as` clause is load-bearing here for a second reason: it
175
+ // runs before the value type, so `ToJSONField` never reaches the machinery,
176
+ // where it would recurse through `restore(json: ToJSON<this>)` past TypeScript
177
+ // 7's instantiation limit. Probing `IsOptionalKey` first is deliberate too
178
+ // `DataKey` then runs in the taken branch only, once per key not once per half.
179
179
  export type ToJSON<T> =
180
180
  & { -readonly [K in keyof T as IsOptionalKey<T, K> extends true ? never : DataKey<T, K>]-?: ToJSONField<T[K]> }
181
181
  & { -readonly [K in keyof T as IsOptionalKey<T, K> extends true ? DataKey<T, K> : never]?: ToJSONField<Exclude<T[K], undefined>> };
@@ -210,9 +210,11 @@ export type IsNever<T> = [T] extends [never] ? true : false;
210
210
  * - Primitives can be assigned directly
211
211
  * - Schema instances can be assigned from plain objects or Schema instances
212
212
  * - Collections can be assigned from their JSON representations
213
+ *
214
+ * Keys filter through `DataKey` in the `as` clause — see THE RULE above.
213
215
  */
214
216
  export type AssignableProps<T> = {
215
- [K in NonFunctionPropNames<T>]?: AssignableValue<T[K]>
217
+ -readonly [K in keyof T as DataKey<T, K>]?: AssignableValue<T[K]>
216
218
  };
217
219
 
218
220
  /**
@@ -267,19 +269,15 @@ type KeyClass<T, K extends keyof T> =
267
269
  ? (RefHasDefault<T[K]> extends true ? "optional" : "required")
268
270
  : "none";
269
271
 
270
- export type BuilderRequiredKeys<T> = {
271
- [K in keyof T]-?: KeyClass<T, K> extends "required" ? K : never
272
- }[keyof T];
273
-
274
- export type BuilderOptionalKeys<T> = {
275
- [K in keyof T]-?: KeyClass<T, K> extends "optional" ? K : never
276
- }[keyof T];
277
-
278
272
  /**
279
273
  * Constructor/init-props type for a schema() fields map. Required fields
280
274
  * (primitives without `.default()` or `.optional()`, and Schema refs with
281
275
  * non-zero-arg `initialize()`) are `:`; everything else is `?:`.
276
+ *
277
+ * Split by `KeyClass` in the `as` clause — see THE RULE above. `-?` matters
278
+ * under `strictNullChecks: false`: an optional key in the fields map still
279
+ * classifies as "required" there, and would otherwise inherit the `?`.
282
280
  */
283
281
  export type BuilderInitProps<T> =
284
- & { [K in BuilderRequiredKeys<T>]: AssignableValue<FieldValue<T[K]>> }
285
- & { [K in BuilderOptionalKeys<T>]?: AssignableValue<Exclude<FieldValue<T[K]>, undefined>> };
282
+ & { -readonly [K in keyof T as KeyClass<T, K> extends "required" ? K : never]-?: AssignableValue<FieldValue<T[K]>> }
283
+ & { -readonly [K in keyof T as KeyClass<T, K> extends "optional" ? K : never]?: AssignableValue<Exclude<FieldValue<T[K]>, undefined>> };