@colyseus/schema 4.0.29 → 4.0.31
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/encoder/ChangeTree.d.ts +9 -0
- package/build/index.cjs +100 -0
- package/build/index.cjs.map +1 -1
- package/build/index.js +100 -0
- package/build/index.mjs +100 -0
- package/build/index.mjs.map +1 -1
- package/build/types/custom/ArraySchema.d.ts +16 -0
- package/build/types/custom/MapSchema.d.ts +17 -0
- package/package.json +1 -1
- package/src/encoder/ChangeTree.ts +27 -0
- package/src/types/custom/ArraySchema.ts +39 -0
- package/src/types/custom/MapSchema.ts +33 -0
|
@@ -32,6 +32,22 @@ export declare class ArraySchema<V = any> implements Array<V>, Collection<number
|
|
|
32
32
|
constructor(...items: V[]);
|
|
33
33
|
set length(newLength: number);
|
|
34
34
|
get length(): number;
|
|
35
|
+
/**
|
|
36
|
+
* Re-point children at their wire slot. `ChangeTree.parentIndex` caches
|
|
37
|
+
* the slot a child holds in `tmpItems`, and StateView addresses per-view
|
|
38
|
+
* ADD/DELETE with it — so a reorder that leaves it behind aims those ops
|
|
39
|
+
* at whichever element inherited the slot (issue #231).
|
|
40
|
+
*
|
|
41
|
+
* The filter check is a correctness boundary, not a tunable: StateView is
|
|
42
|
+
* the only reader, and an array without `filteredChanges` never has a slot
|
|
43
|
+
* read back. Everything else stops at that check instead of walking its
|
|
44
|
+
* children every tick.
|
|
45
|
+
*
|
|
46
|
+
* Callers name the lowest slot that moved as `from`. Compaction cannot, so
|
|
47
|
+
* it hands over the pre-compaction layout as `staged` and the unchanged
|
|
48
|
+
* prefix is skipped instead. Either way tail churn walks nothing.
|
|
49
|
+
*/
|
|
50
|
+
protected $reindexChildren(from: number, staged?: V[]): void;
|
|
35
51
|
push(...values: V[]): number;
|
|
36
52
|
/**
|
|
37
53
|
* Removes the last element from an array and returns it.
|
|
@@ -33,6 +33,23 @@ export declare class MapSchema<V = any, K extends string = string> implements Ma
|
|
|
33
33
|
static get [Symbol.species](): typeof MapSchema;
|
|
34
34
|
set(key: K, value: V): this;
|
|
35
35
|
get(key: K): V | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the value for `key` if present. Otherwise inserts `defaultValue`
|
|
38
|
+
* (tracked as an ADD change, like `set()`) and returns it.
|
|
39
|
+
*
|
|
40
|
+
* Mirrors `Map.prototype.getOrInsert` (TC39 "upsert" proposal, typed in
|
|
41
|
+
* TypeScript 6's standard library).
|
|
42
|
+
*/
|
|
43
|
+
getOrInsert(key: K, defaultValue: V): V;
|
|
44
|
+
/**
|
|
45
|
+
* Returns the value for `key` if present. Otherwise computes a value via
|
|
46
|
+
* `callbackfn(key)`, inserts it (tracked as an ADD change, like `set()`)
|
|
47
|
+
* and returns it. The callback is only invoked when the key is missing.
|
|
48
|
+
*
|
|
49
|
+
* Mirrors `Map.prototype.getOrInsertComputed` (TC39 "upsert" proposal,
|
|
50
|
+
* typed in TypeScript 6's standard library).
|
|
51
|
+
*/
|
|
52
|
+
getOrInsertComputed(key: K, callbackfn: (key: K) => V): V;
|
|
36
53
|
delete(key: K): boolean;
|
|
37
54
|
clear(): void;
|
|
38
55
|
has(key: K): boolean;
|
package/package.json
CHANGED
|
@@ -652,6 +652,33 @@ export class ChangeTree<T extends Ref = any> {
|
|
|
652
652
|
};
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
/**
|
|
656
|
+
* Move `parent`'s existing chain entry to `index`, skipping the work
|
|
657
|
+
* `addParent` does. `parent` must already be a parent of this tree.
|
|
658
|
+
*
|
|
659
|
+
* Called by collections whose wire slots shift (ArraySchema): StateView
|
|
660
|
+
* addresses per-view ADD/DELETE by that index, so it has to follow the
|
|
661
|
+
* element it names.
|
|
662
|
+
*/
|
|
663
|
+
setParentIndex(parent: Ref, index: number) {
|
|
664
|
+
const chain = this.parentChain;
|
|
665
|
+
if (chain === undefined) { return; }
|
|
666
|
+
|
|
667
|
+
if (chain.next === undefined) {
|
|
668
|
+
chain.index = index; // sole parent, so it is `parent`
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
// Shared instance — move only the entry `parent` owns. Matching goes
|
|
672
|
+
// through `$changes` because ArraySchema arrives proxied (see
|
|
673
|
+
// removeParent below).
|
|
674
|
+
for (let entry = chain; entry !== undefined; entry = entry.next) {
|
|
675
|
+
if (entry.ref[$changes] === parent[$changes]) {
|
|
676
|
+
entry.index = index;
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
655
682
|
/**
|
|
656
683
|
* Remove a parent from the chain
|
|
657
684
|
* @param parent - The parent to remove
|
|
@@ -186,6 +186,34 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
186
186
|
return this.items.length;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Re-point children at their wire slot. `ChangeTree.parentIndex` caches
|
|
191
|
+
* the slot a child holds in `tmpItems`, and StateView addresses per-view
|
|
192
|
+
* ADD/DELETE with it — so a reorder that leaves it behind aims those ops
|
|
193
|
+
* at whichever element inherited the slot (issue #231).
|
|
194
|
+
*
|
|
195
|
+
* The filter check is a correctness boundary, not a tunable: StateView is
|
|
196
|
+
* the only reader, and an array without `filteredChanges` never has a slot
|
|
197
|
+
* read back. Everything else stops at that check instead of walking its
|
|
198
|
+
* children every tick.
|
|
199
|
+
*
|
|
200
|
+
* Callers name the lowest slot that moved as `from`. Compaction cannot, so
|
|
201
|
+
* it hands over the pre-compaction layout as `staged` and the unchanged
|
|
202
|
+
* prefix is skipped instead. Either way tail churn walks nothing.
|
|
203
|
+
*/
|
|
204
|
+
protected $reindexChildren(from: number, staged?: V[]) {
|
|
205
|
+
if (this[$changes].filteredChanges === undefined) { return; } // nothing will read the cache
|
|
206
|
+
if (typeof this[$childType] === "string") { return; } // primitives have no child tree
|
|
207
|
+
const tmpItems = this.tmpItems;
|
|
208
|
+
const length = tmpItems.length;
|
|
209
|
+
if (staged !== undefined) {
|
|
210
|
+
while (from < length && tmpItems[from] === staged[from]) { from++; }
|
|
211
|
+
}
|
|
212
|
+
for (let i = from; i < length; i++) {
|
|
213
|
+
tmpItems[i]?.[$changes]?.setParentIndex(this, i);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
189
217
|
push(...values: V[]) {
|
|
190
218
|
let length = this.tmpItems.length;
|
|
191
219
|
|
|
@@ -348,6 +376,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
348
376
|
this[$changes].operation(OPERATION.REVERSE);
|
|
349
377
|
this.items.reverse();
|
|
350
378
|
this.tmpItems.reverse();
|
|
379
|
+
this.$reindexChildren(0);
|
|
351
380
|
return this;
|
|
352
381
|
}
|
|
353
382
|
|
|
@@ -400,6 +429,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
400
429
|
sortedItems.forEach((_, i) => changeTree.change(i, OPERATION.REPLACE));
|
|
401
430
|
|
|
402
431
|
this.tmpItems.sort(compareFn);
|
|
432
|
+
this.$reindexChildren(0);
|
|
403
433
|
|
|
404
434
|
this.isMovingItems = false;
|
|
405
435
|
return this;
|
|
@@ -519,6 +549,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
519
549
|
});
|
|
520
550
|
|
|
521
551
|
this.tmpItems.unshift(...items);
|
|
552
|
+
this.$reindexChildren(0); // from 0: nothing above placed the new items either
|
|
522
553
|
|
|
523
554
|
return this.items.unshift(...items);
|
|
524
555
|
}
|
|
@@ -845,7 +876,15 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V>, IR
|
|
|
845
876
|
}
|
|
846
877
|
|
|
847
878
|
protected [$onEncodeEnd]() {
|
|
879
|
+
const staged = this.tmpItems;
|
|
848
880
|
this.tmpItems = this.items.slice();
|
|
881
|
+
|
|
882
|
+
// Compaction just closed the staged holes — everything above the
|
|
883
|
+
// lowest one slid down a slot. There is no cheap "were there any"
|
|
884
|
+
// test to gate this on: `deletedIndexes` is an object here, and a
|
|
885
|
+
// `for...in` probe measured slower than the prefix scan it skips.
|
|
886
|
+
this.$reindexChildren(0, staged);
|
|
887
|
+
|
|
849
888
|
this.deletedIndexes = {};
|
|
850
889
|
}
|
|
851
890
|
|
|
@@ -152,6 +152,39 @@ export class MapSchema<V=any, K extends string = string> implements Map<K, V>, C
|
|
|
152
152
|
return this.$items.get(key);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Returns the value for `key` if present. Otherwise inserts `defaultValue`
|
|
157
|
+
* (tracked as an ADD change, like `set()`) and returns it.
|
|
158
|
+
*
|
|
159
|
+
* Mirrors `Map.prototype.getOrInsert` (TC39 "upsert" proposal, typed in
|
|
160
|
+
* TypeScript 6's standard library).
|
|
161
|
+
*/
|
|
162
|
+
getOrInsert(key: K, defaultValue: V): V {
|
|
163
|
+
if (this.$items.has(key)) {
|
|
164
|
+
return this.$items.get(key);
|
|
165
|
+
}
|
|
166
|
+
this.set(key, defaultValue);
|
|
167
|
+
return defaultValue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns the value for `key` if present. Otherwise computes a value via
|
|
172
|
+
* `callbackfn(key)`, inserts it (tracked as an ADD change, like `set()`)
|
|
173
|
+
* and returns it. The callback is only invoked when the key is missing.
|
|
174
|
+
*
|
|
175
|
+
* Mirrors `Map.prototype.getOrInsertComputed` (TC39 "upsert" proposal,
|
|
176
|
+
* typed in TypeScript 6's standard library).
|
|
177
|
+
*/
|
|
178
|
+
getOrInsertComputed(key: K, callbackfn: (key: K) => V): V {
|
|
179
|
+
if (this.$items.has(key)) {
|
|
180
|
+
return this.$items.get(key);
|
|
181
|
+
}
|
|
182
|
+
const value = callbackfn(key);
|
|
183
|
+
// per spec: overwrites even if callbackfn itself inserted `key`
|
|
184
|
+
this.set(key, value);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
|
|
155
188
|
delete(key: K) {
|
|
156
189
|
if (!this.$items.has(key)) {
|
|
157
190
|
return false;
|