@colyseus/schema 3.0.22 → 3.0.24

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.
@@ -3,16 +3,23 @@ import { $changes, $fieldIndexesByViewTag, $viewFieldIndexes } from "../types/sy
3
3
  import { DEFAULT_VIEW_TAG } from "../annotations";
4
4
  import { OPERATION } from "../encoding/spec";
5
5
  import { Metadata } from "../Metadata";
6
+ import { spliceOne } from "../types/utils";
6
7
 
7
- export function createView() {
8
- return new StateView();
8
+ export function createView(iterable: boolean = false) {
9
+ return new StateView(iterable);
9
10
  }
10
11
 
11
12
  export class StateView {
13
+ /**
14
+ * Iterable list of items that are visible to this view
15
+ * (Available only if constructed with `iterable: true`)
16
+ */
17
+ items: Ref[];
18
+
12
19
  /**
13
20
  * List of ChangeTree's that are visible to this view
14
21
  */
15
- items: WeakSet<ChangeTree> = new WeakSet<ChangeTree>();
22
+ visible: WeakSet<ChangeTree> = new WeakSet<ChangeTree>();
16
23
 
17
24
  /**
18
25
  * List of ChangeTree's that are invisible to this view
@@ -27,17 +34,44 @@ export class StateView {
27
34
  */
28
35
  changes = new Map<number, IndexedOperations>();
29
36
 
37
+ constructor(public iterable: boolean = false) {
38
+ if (iterable) {
39
+ this.items = [];
40
+ }
41
+ }
42
+
30
43
  // TODO: allow to set multiple tags at once
31
44
  add(obj: Ref, tag: number = DEFAULT_VIEW_TAG, checkIncludeParent: boolean = true) {
32
- if (!obj?.[$changes]) {
45
+ const changeTree: ChangeTree = obj?.[$changes];
46
+
47
+ if (!changeTree) {
33
48
  console.warn("StateView#add(), invalid object:", obj);
34
49
  return this;
50
+
51
+ } else if (
52
+ !changeTree.parent &&
53
+ changeTree.refId !== 0 // allow root object
54
+ ) {
55
+ /**
56
+ * TODO: can we avoid this?
57
+ *
58
+ * When the "parent" structure has the @view() tag, it is currently
59
+ * not possible to identify it has to be added to the view as well
60
+ * (this.addParentOf() is not called).
61
+ */
62
+ throw new Error(
63
+ `Cannot add a detached instance to the StateView. Make sure to assign the "${changeTree.ref.constructor.name}" instance to the state before calling view.add()`
64
+ );
35
65
  }
36
66
 
37
67
  // FIXME: ArraySchema/MapSchema do not have metadata
38
68
  const metadata: Metadata = obj.constructor[Symbol.metadata];
39
- const changeTree: ChangeTree = obj[$changes];
40
- this.items.add(changeTree);
69
+ this.visible.add(changeTree);
70
+
71
+ // add to iterable list (only the explicitly added items)
72
+ if (this.iterable && checkIncludeParent) {
73
+ this.items.push(obj);
74
+ }
41
75
 
42
76
  // add parent ChangeTree's
43
77
  // - if it was invisible to this view
@@ -123,9 +157,9 @@ export class StateView {
123
157
  const changeTree = childChangeTree.parent[$changes];
124
158
  const parentIndex = childChangeTree.parentIndex;
125
159
 
126
- if (!this.items.has(changeTree)) {
160
+ if (!this.visible.has(changeTree)) {
127
161
  // view must have all "changeTree" parent tree
128
- this.items.add(changeTree);
162
+ this.visible.add(changeTree);
129
163
 
130
164
  // add parent's parent
131
165
  const parentChangeTree: ChangeTree = changeTree.parent?.[$changes];
@@ -162,14 +196,24 @@ export class StateView {
162
196
  }
163
197
  }
164
198
 
165
- remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {
199
+ remove(obj: Ref, tag?: number): this; // hide _isClear parameter from public API
200
+ remove(obj: Ref, tag?: number, _isClear?: boolean): this;
201
+ remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG, _isClear: boolean = false): this {
166
202
  const changeTree: ChangeTree = obj[$changes];
167
203
  if (!changeTree) {
168
204
  console.warn("StateView#remove(), invalid object:", obj);
169
205
  return this;
170
206
  }
171
207
 
172
- this.items.delete(changeTree);
208
+ this.visible.delete(changeTree);
209
+
210
+ // remove from iterable list
211
+ if (
212
+ this.iterable &&
213
+ !_isClear // no need to remove during clear(), as it will be cleared entirely
214
+ ) {
215
+ spliceOne(this.items, this.items.indexOf(obj));
216
+ }
173
217
 
174
218
  const ref = changeTree.ref;
175
219
  const metadata: Metadata = ref.constructor[Symbol.metadata]; // ArraySchema/MapSchema do not have metadata
@@ -227,11 +271,24 @@ export class StateView {
227
271
  }
228
272
 
229
273
  has(obj: Ref) {
230
- return this.items.has(obj[$changes]);
274
+ return this.visible.has(obj[$changes]);
231
275
  }
232
276
 
233
277
  hasTag(ob: Ref, tag: number = DEFAULT_VIEW_TAG) {
234
278
  const tags = this.tags?.get(ob[$changes]);
235
279
  return tags?.has(tag) ?? false;
236
280
  }
237
- }
281
+
282
+ clear() {
283
+ if (!this.iterable) {
284
+ throw new Error("StateView#clear() is only available for iterable StateView's. Use StateView(iterable: true) constructor.");
285
+ }
286
+
287
+ for (let i = 0, l = this.items.length; i < l; i++) {
288
+ this.remove(this.items[i], DEFAULT_VIEW_TAG, true);
289
+ }
290
+
291
+ // clear items array
292
+ this.items.length = 0;
293
+ }
294
+ }
@@ -42,7 +42,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
42
42
  !view ||
43
43
  typeof (ref[$childType]) === "string" ||
44
44
  // view.items.has(ref[$getByIndex](index)[$changes])
45
- view.items.has(ref['tmpItems'][index]?.[$changes])
45
+ view.visible.has(ref['tmpItems'][index]?.[$changes])
46
46
  );
47
47
  }
48
48
 
@@ -33,7 +33,7 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
33
33
  return (
34
34
  !view ||
35
35
  typeof (ref[$childType]) === "string" ||
36
- view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
36
+ view.visible.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
37
37
  );
38
38
  }
39
39
 
@@ -34,7 +34,7 @@ export class MapSchema<V=any, K extends string = string> implements Map<K, V>, C
34
34
  return (
35
35
  !view ||
36
36
  typeof (ref[$childType]) === "string" ||
37
- view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
37
+ view.visible.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
38
38
  );
39
39
  }
40
40
 
@@ -31,7 +31,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
31
31
  return (
32
32
  !view ||
33
33
  typeof (ref[$childType]) === "string" ||
34
- view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
34
+ view.visible.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
35
35
  );
36
36
  }
37
37