@colyseus/schema 3.0.0-alpha.30 → 3.0.0-alpha.32

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 (77) hide show
  1. package/build/cjs/index.js +389 -354
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/index.mjs +389 -354
  4. package/build/esm/index.mjs.map +1 -1
  5. package/build/umd/index.js +389 -354
  6. package/lib/Metadata.d.ts +14 -5
  7. package/lib/Metadata.js +49 -20
  8. package/lib/Metadata.js.map +1 -1
  9. package/lib/Reflection.js +4 -13
  10. package/lib/Reflection.js.map +1 -1
  11. package/lib/Schema.js +26 -39
  12. package/lib/Schema.js.map +1 -1
  13. package/lib/annotations.d.ts +1 -2
  14. package/lib/annotations.js +58 -52
  15. package/lib/annotations.js.map +1 -1
  16. package/lib/bench_encode.js +25 -22
  17. package/lib/bench_encode.js.map +1 -1
  18. package/lib/decoder/DecodeOperation.js +7 -9
  19. package/lib/decoder/DecodeOperation.js.map +1 -1
  20. package/lib/decoder/ReferenceTracker.js +3 -2
  21. package/lib/decoder/ReferenceTracker.js.map +1 -1
  22. package/lib/decoder/strategy/StateCallbacks.js +4 -3
  23. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  24. package/lib/encoder/ChangeTree.d.ts +8 -7
  25. package/lib/encoder/ChangeTree.js +135 -117
  26. package/lib/encoder/ChangeTree.js.map +1 -1
  27. package/lib/encoder/EncodeOperation.d.ts +1 -4
  28. package/lib/encoder/EncodeOperation.js +17 -47
  29. package/lib/encoder/EncodeOperation.js.map +1 -1
  30. package/lib/encoder/Encoder.js +18 -6
  31. package/lib/encoder/Encoder.js.map +1 -1
  32. package/lib/encoder/Root.d.ts +2 -2
  33. package/lib/encoder/Root.js +18 -6
  34. package/lib/encoder/Root.js.map +1 -1
  35. package/lib/encoder/StateView.js +3 -3
  36. package/lib/encoder/StateView.js.map +1 -1
  37. package/lib/encoding/assert.d.ts +2 -1
  38. package/lib/encoding/assert.js +2 -2
  39. package/lib/encoding/assert.js.map +1 -1
  40. package/lib/index.d.ts +1 -2
  41. package/lib/index.js +11 -10
  42. package/lib/index.js.map +1 -1
  43. package/lib/types/TypeContext.js +7 -14
  44. package/lib/types/TypeContext.js.map +1 -1
  45. package/lib/types/custom/ArraySchema.js +6 -0
  46. package/lib/types/custom/ArraySchema.js.map +1 -1
  47. package/lib/types/custom/CollectionSchema.js +1 -0
  48. package/lib/types/custom/CollectionSchema.js.map +1 -1
  49. package/lib/types/custom/MapSchema.js +5 -0
  50. package/lib/types/custom/MapSchema.js.map +1 -1
  51. package/lib/types/custom/SetSchema.js +1 -0
  52. package/lib/types/custom/SetSchema.js.map +1 -1
  53. package/lib/types/symbols.d.ts +1 -0
  54. package/lib/types/symbols.js +2 -1
  55. package/lib/types/symbols.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/Metadata.ts +60 -29
  58. package/src/Reflection.ts +5 -15
  59. package/src/Schema.ts +33 -45
  60. package/src/annotations.ts +75 -67
  61. package/src/bench_encode.ts +29 -27
  62. package/src/decoder/DecodeOperation.ts +12 -11
  63. package/src/decoder/ReferenceTracker.ts +3 -2
  64. package/src/decoder/strategy/StateCallbacks.ts +4 -3
  65. package/src/encoder/ChangeTree.ts +154 -138
  66. package/src/encoder/EncodeOperation.ts +42 -62
  67. package/src/encoder/Encoder.ts +25 -8
  68. package/src/encoder/Root.ts +23 -6
  69. package/src/encoder/StateView.ts +4 -4
  70. package/src/encoding/assert.ts +4 -3
  71. package/src/index.ts +1 -4
  72. package/src/types/TypeContext.ts +10 -15
  73. package/src/types/custom/ArraySchema.ts +8 -0
  74. package/src/types/custom/CollectionSchema.ts +1 -0
  75. package/src/types/custom/MapSchema.ts +6 -0
  76. package/src/types/custom/SetSchema.ts +1 -0
  77. package/src/types/symbols.ts +2 -0
@@ -1,6 +1,6 @@
1
1
  import type { Schema } from "../Schema";
2
2
  import { TypeContext } from "../types/TypeContext";
3
- import { $changes, $encoder, $filter } from "../types/symbols";
3
+ import { $changes, $encoder, $filter, $isNew, $onEncodeEnd } from "../types/symbols";
4
4
 
5
5
  import * as encode from "../encoding/encode";
6
6
  import type { Iterator } from "../encoding/decode";
@@ -55,12 +55,12 @@ export class Encoder<T extends Schema = any> {
55
55
  const hasView = (view !== undefined);
56
56
  const rootChangeTree = this.state[$changes];
57
57
 
58
- const changeTreesIterator = changeTrees.entries();
58
+ const shouldClearChanges = !isEncodeAll && !hasView;
59
59
 
60
- for (const [changeTree, changes] of changeTreesIterator) {
60
+ for (const [changeTree, changes] of changeTrees.entries()) {
61
61
  const ref = changeTree.ref;
62
62
 
63
- const ctor = ref['constructor'];
63
+ const ctor = ref.constructor;
64
64
  const encoder = ctor[$encoder];
65
65
  const filter = ctor[$filter];
66
66
 
@@ -88,9 +88,7 @@ export class Encoder<T extends Schema = any> {
88
88
  encode.number(buffer, changeTree.refId, it);
89
89
  }
90
90
 
91
- const changesIterator = changes.entries();
92
-
93
- for (const [fieldIndex, operation] of changesIterator) {
91
+ for (const [fieldIndex, operation] of changes.entries()) {
94
92
  //
95
93
  // first pass (encodeAll), identify "filtered" operations without encoding them
96
94
  // they will be encoded per client, based on their view.
@@ -117,6 +115,17 @@ export class Encoder<T extends Schema = any> {
117
115
 
118
116
  encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
119
117
  }
118
+
119
+ // if (shouldClearChanges) {
120
+ // // changeTree.endEncode();
121
+ // changeTree.changes.clear();
122
+
123
+ // // ArraySchema and MapSchema have a custom "encode end" method
124
+ // changeTree.ref[$onEncodeEnd]?.();
125
+
126
+ // // Not a new instance anymore
127
+ // delete changeTree[$isNew];
128
+ // }
120
129
  }
121
130
 
122
131
  if (it.offset > buffer.byteLength) {
@@ -143,7 +152,7 @@ export class Encoder<T extends Schema = any> {
143
152
  //
144
153
  // only clear changes after making sure buffer resize is not required.
145
154
  //
146
- if (!isEncodeAll && !hasView) {
155
+ if (shouldClearChanges) {
147
156
  //
148
157
  // FIXME: avoid iterating over change trees twice.
149
158
  //
@@ -251,6 +260,14 @@ export class Encoder<T extends Schema = any> {
251
260
  const changeTreesIterator = changeTrees.entries();
252
261
  for (const [changeTree, _] of changeTreesIterator) {
253
262
  changeTree.endEncode();
263
+ // changeTree.changes.clear();
264
+
265
+ // // ArraySchema and MapSchema have a custom "encode end" method
266
+ // changeTree.ref[$onEncodeEnd]?.();
267
+
268
+ // // Not a new instance anymore
269
+ // delete changeTree[$isNew];
270
+
254
271
  }
255
272
  }
256
273
 
@@ -21,13 +21,28 @@ export class Root {
21
21
  }
22
22
 
23
23
  add(changeTree: ChangeTree) {
24
- const refCount = this.refCount.get(changeTree) || 0;
25
- this.refCount.set(changeTree, refCount + 1);
24
+ const previousRefCount = this.refCount.get(changeTree);
25
+
26
+ if (previousRefCount === 0) {
27
+ //
28
+ // When a ChangeTree is re-added, it means that it was previously removed.
29
+ // We need to re-add all changes to the `changes` map.
30
+ //
31
+ changeTree.allChanges.forEach((operation, index) => {
32
+ changeTree.changes.set(index, operation);
33
+ });
34
+ }
35
+
36
+ const refCount = (previousRefCount || 0) + 1;
37
+ this.refCount.set(changeTree, refCount);
38
+
39
+ return refCount;
26
40
  }
27
41
 
28
42
  remove(changeTree: ChangeTree) {
29
- const refCount = this.refCount.get(changeTree);
30
- if (refCount <= 1) {
43
+ const refCount = (this.refCount.get(changeTree)) - 1;
44
+
45
+ if (refCount <= 0) {
31
46
  this.allChanges.delete(changeTree);
32
47
  this.changes.delete(changeTree);
33
48
 
@@ -36,13 +51,15 @@ export class Root {
36
51
  this.filteredChanges.delete(changeTree);
37
52
  }
38
53
 
39
- this.refCount.delete(changeTree);
54
+ this.refCount.set(changeTree, 0);
40
55
 
41
56
  } else {
42
- this.refCount.set(changeTree, refCount - 1);
57
+ this.refCount.set(changeTree, refCount);
43
58
  }
44
59
 
45
60
  changeTree.forEachChild((child, _) => this.remove(child));
61
+
62
+ return refCount;
46
63
  }
47
64
 
48
65
  clear() {
@@ -34,7 +34,7 @@ export class StateView {
34
34
  return this;
35
35
  }
36
36
 
37
- // FIXME: ArraySchema/MapSchema does not have metadata
37
+ // FIXME: ArraySchema/MapSchema do not have metadata
38
38
  const metadata: Metadata = obj.constructor[Symbol.metadata];
39
39
  const changeTree: ChangeTree = obj[$changes];
40
40
  this.items.add(changeTree);
@@ -84,7 +84,7 @@ export class StateView {
84
84
  : changeTree.allChanges;
85
85
 
86
86
  changeSet.forEach((op, index) => {
87
- const tagAtIndex = metadata?.[metadata?.[index]].tag;
87
+ const tagAtIndex = metadata?.[index].tag;
88
88
  if (
89
89
  (
90
90
  isInvisible || // if "invisible", include all
@@ -101,7 +101,7 @@ export class StateView {
101
101
  // Add children of this ChangeTree to this view
102
102
  changeTree.forEachChild((change, index) => {
103
103
  // Do not ADD children that don't have the same tag
104
- if (metadata && metadata[metadata[index]].tag !== tag) {
104
+ if (metadata && metadata[index].tag !== tag) {
105
105
  return;
106
106
  }
107
107
  this.add(change.ref, tag, false);
@@ -166,7 +166,7 @@ export class StateView {
166
166
  let changes = this.changes.get(changeTree);
167
167
  if (changes === undefined) {
168
168
  changes = new Map<number, OPERATION>();
169
- this.changes.set(changeTree, changes)
169
+ this.changes.set(changeTree, changes);
170
170
  }
171
171
 
172
172
  if (tag === DEFAULT_VIEW_TAG) {
@@ -3,6 +3,7 @@ import { CollectionSchema } from "../types/custom/CollectionSchema";
3
3
  import { MapSchema } from "../types/custom/MapSchema";
4
4
  import { SetSchema } from "../types/custom/SetSchema";
5
5
  import { ArraySchema } from "../types/custom/ArraySchema";
6
+ import type { Ref } from "../encoder/ChangeTree";
6
7
 
7
8
  export class EncodeSchemaError extends Error {}
8
9
 
@@ -43,16 +44,16 @@ export function assertType(value: any, type: string, klass: Schema, field: strin
43
44
  }
44
45
 
45
46
  export function assertInstanceType(
46
- value: Schema,
47
+ value: Ref,
47
48
  type: typeof Schema
48
49
  | typeof ArraySchema
49
50
  | typeof MapSchema
50
51
  | typeof CollectionSchema
51
52
  | typeof SetSchema,
52
- klass: Schema,
53
+ instance: Ref,
53
54
  field: string | number,
54
55
  ) {
55
56
  if (!(value instanceof type)) {
56
- throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && (value as any).constructor.name}' was provided in ${klass.constructor.name}#${field}`);
57
+ throw new EncodeSchemaError(`a '${type.name}' was expected, but '${value && (value as any).constructor.name}' was provided in ${instance.constructor.name}#${field}`);
57
58
  }
58
59
  }
package/src/index.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  export { Schema } from "./Schema";
2
2
  export type { DataChange } from "./decoder/DecodeOperation";
3
-
4
- import { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType } from "./types/symbols";
5
- export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType };
6
-
7
3
  export type { ToJSON } from "./types/HelperTypes";
8
4
 
9
5
  import { MapSchema } from "./types/custom/MapSchema"
@@ -30,6 +26,7 @@ registerType("collection", { constructor: CollectionSchema, });
30
26
  export { dumpChanges } from "./utils";
31
27
 
32
28
  // Encoder / Decoder
29
+ export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType } from "./types/symbols";
33
30
  export type { Iterator } from "./encoding/decode";
34
31
  import * as encode from "./encoding/encode";
35
32
  import * as decode from "./encoding/decode";
@@ -84,19 +84,11 @@ export class TypeContext {
84
84
  this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
85
85
  }
86
86
 
87
- for (const field in metadata) {
88
- // //
89
- // // Modify the field's metadata to include the parent field's view tag
90
- // //
91
- // if (
92
- // parentFieldViewTag !== undefined &&
93
- // metadata[field].tag === undefined
94
- // ) {
95
- // metadata[field].tag = parentFieldViewTag;
96
- // }
97
-
98
- const fieldType = metadata[field].type;
99
- const viewTag = metadata[field].tag;
87
+ for (const fieldIndex in metadata) {
88
+ const index = fieldIndex as any as number;
89
+
90
+ const fieldType = metadata[index].type;
91
+ const viewTag = metadata[index].tag;
100
92
 
101
93
  if (typeof (fieldType) === "string") {
102
94
  continue;
@@ -104,10 +96,13 @@ export class TypeContext {
104
96
 
105
97
  if (Array.isArray(fieldType)) {
106
98
  const type = fieldType[0];
99
+
100
+ // skip primitive types
107
101
  if (type === "string") {
108
102
  continue;
109
103
  }
110
- this.discoverTypes(type as typeof Schema, metadata[field].index, viewTag);
104
+
105
+ this.discoverTypes(type as typeof Schema, index, viewTag);
111
106
 
112
107
  } else if (typeof (fieldType) === "function") {
113
108
  this.discoverTypes(fieldType as typeof Schema, viewTag);
@@ -120,7 +115,7 @@ export class TypeContext {
120
115
  continue;
121
116
  }
122
117
 
123
- this.discoverTypes(type as typeof Schema, metadata[field].index, viewTag);
118
+ this.discoverTypes(type as typeof Schema, index, viewTag);
124
119
  }
125
120
  }
126
121
  }
@@ -8,6 +8,7 @@ import { Collection } from "../HelperTypes";
8
8
  import { encodeArray } from "../../encoder/EncodeOperation";
9
9
  import { decodeArray } from "../../decoder/DecodeOperation";
10
10
  import type { StateView } from "../../encoder/StateView";
11
+ import { assertInstanceType } from "../../encoding/assert";
11
12
 
12
13
  const DEFAULT_SORT = (a: any, b: any) => {
13
14
  const A = a.toString();
@@ -85,6 +86,8 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
85
86
 
86
87
  } else {
87
88
  if (setValue[$changes]) {
89
+ assertInstanceType(setValue, obj[$childType] as typeof Schema, obj, key);
90
+
88
91
  if (obj.items[key as unknown as number] !== undefined) {
89
92
  if (setValue[$changes][$isNew]) {
90
93
  this[$changes].indexedOperation(Number(key), OPERATION.MOVE_AND_ADD);
@@ -99,6 +102,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
99
102
  } else if (setValue[$changes][$isNew]) {
100
103
  this[$changes].indexedOperation(Number(key), OPERATION.ADD);
101
104
  }
105
+
102
106
  } else {
103
107
  obj.$changeAt(Number(key), setValue);
104
108
  }
@@ -133,6 +137,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
133
137
  });
134
138
 
135
139
  this[$changes] = new ChangeTree(proxy);
140
+ this[$changes].indexes = {};
136
141
  this.push.apply(this, items);
137
142
 
138
143
  return proxy;
@@ -159,6 +164,9 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
159
164
  // skip null values
160
165
  if (value === undefined || value === null) {
161
166
  return;
167
+
168
+ } else if (typeof (value) === "object" && this[$childType]) {
169
+ assertInstanceType(value as any, this[$childType] as typeof Schema, this, i);
162
170
  }
163
171
 
164
172
  const changeTree = this[$changes];
@@ -41,6 +41,7 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
41
41
 
42
42
  constructor (initialValues?: Array<V>) {
43
43
  this[$changes] = new ChangeTree(this);
44
+ this[$changes].indexes = {};
44
45
 
45
46
  if (initialValues) {
46
47
  initialValues.forEach((v) => this.add(v));
@@ -6,6 +6,8 @@ import { Collection } from "../HelperTypes";
6
6
  import { decodeKeyValueOperation } from "../../decoder/DecodeOperation";
7
7
  import { encodeKeyValueOperation } from "../../encoder/EncodeOperation";
8
8
  import type { StateView } from "../../encoder/StateView";
9
+ import type { Schema } from "../../Schema";
10
+ import { assertInstanceType } from "../../encoding/assert";
9
11
 
10
12
  export class MapSchema<V=any, K extends string = string> implements Map<K, V>, Collection<K, V, [K, V]> {
11
13
  protected childType: new () => V;
@@ -39,6 +41,7 @@ export class MapSchema<V=any, K extends string = string> implements Map<K, V>, C
39
41
 
40
42
  constructor (initialValues?: Map<K, V> | Record<K, V>) {
41
43
  this[$changes] = new ChangeTree(this);
44
+ this[$changes].indexes = {};
42
45
 
43
46
  if (initialValues) {
44
47
  if (
@@ -71,6 +74,9 @@ export class MapSchema<V=any, K extends string = string> implements Map<K, V>, C
71
74
  set(key: K, value: V) {
72
75
  if (value === undefined || value === null) {
73
76
  throw new Error(`MapSchema#set('${key}', ${value}): trying to set ${value} value on '${key}'.`);
77
+
78
+ } else if (typeof(value) === "object" && this[$childType]) {
79
+ assertInstanceType(value as any, this[$childType] as typeof Schema, this, key);
74
80
  }
75
81
 
76
82
  // Force "key" as string
@@ -40,6 +40,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
40
40
 
41
41
  constructor (initialValues?: Array<V>) {
42
42
  this[$changes] = new ChangeTree(this);
43
+ this[$changes].indexes = {};
43
44
 
44
45
  if (initialValues) {
45
46
  initialValues.forEach((v) => this.add(v));
@@ -7,6 +7,8 @@ export const $filter = Symbol("$filter");
7
7
  export const $getByIndex = Symbol("$getByIndex");
8
8
  export const $deleteByIndex = Symbol("$deleteByIndex");
9
9
 
10
+ export const $descriptors = Symbol("$descriptors");
11
+
10
12
  /**
11
13
  * Used to hold ChangeTree instances whitin the structures
12
14
  */