@colyseus/schema 3.0.0-alpha.3 → 3.0.0-alpha.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.
Files changed (134) hide show
  1. package/README.md +131 -61
  2. package/build/cjs/index.js +966 -563
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +965 -562
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +966 -563
  7. package/lib/Metadata.d.ts +15 -4
  8. package/lib/Metadata.js +86 -18
  9. package/lib/Metadata.js.map +1 -1
  10. package/lib/Reflection.d.ts +2 -3
  11. package/lib/Reflection.js +24 -28
  12. package/lib/Reflection.js.map +1 -1
  13. package/lib/Schema.d.ts +2 -2
  14. package/lib/Schema.js +28 -41
  15. package/lib/Schema.js.map +1 -1
  16. package/lib/annotations.d.ts +1 -21
  17. package/lib/annotations.js +73 -153
  18. package/lib/annotations.js.map +1 -1
  19. package/lib/bench_encode.d.ts +1 -0
  20. package/lib/bench_encode.js +142 -0
  21. package/lib/bench_encode.js.map +1 -0
  22. package/lib/codegen/api.js +1 -2
  23. package/lib/codegen/api.js.map +1 -1
  24. package/lib/codegen/languages/cpp.js +1 -2
  25. package/lib/codegen/languages/cpp.js.map +1 -1
  26. package/lib/codegen/languages/csharp.js +1 -2
  27. package/lib/codegen/languages/csharp.js.map +1 -1
  28. package/lib/codegen/languages/haxe.js +1 -2
  29. package/lib/codegen/languages/haxe.js.map +1 -1
  30. package/lib/codegen/languages/java.js +1 -2
  31. package/lib/codegen/languages/java.js.map +1 -1
  32. package/lib/codegen/languages/js.js +1 -2
  33. package/lib/codegen/languages/js.js.map +1 -1
  34. package/lib/codegen/languages/lua.js +1 -2
  35. package/lib/codegen/languages/lua.js.map +1 -1
  36. package/lib/codegen/languages/ts.js +1 -2
  37. package/lib/codegen/languages/ts.js.map +1 -1
  38. package/lib/codegen/parser.js +2 -3
  39. package/lib/codegen/parser.js.map +1 -1
  40. package/lib/codegen/types.js +3 -3
  41. package/lib/codegen/types.js.map +1 -1
  42. package/lib/debug.d.ts +1 -0
  43. package/lib/debug.js +52 -0
  44. package/lib/debug.js.map +1 -0
  45. package/lib/decoder/DecodeOperation.d.ts +0 -1
  46. package/lib/decoder/DecodeOperation.js +23 -11
  47. package/lib/decoder/DecodeOperation.js.map +1 -1
  48. package/lib/decoder/Decoder.d.ts +6 -7
  49. package/lib/decoder/Decoder.js +8 -8
  50. package/lib/decoder/Decoder.js.map +1 -1
  51. package/lib/decoder/ReferenceTracker.js +3 -2
  52. package/lib/decoder/ReferenceTracker.js.map +1 -1
  53. package/lib/decoder/strategy/RawChanges.js +1 -2
  54. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  55. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  56. package/lib/decoder/strategy/StateCallbacks.js +75 -65
  57. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  58. package/lib/encoder/ChangeTree.d.ts +9 -19
  59. package/lib/encoder/ChangeTree.js +129 -145
  60. package/lib/encoder/ChangeTree.js.map +1 -1
  61. package/lib/encoder/EncodeOperation.d.ts +1 -5
  62. package/lib/encoder/EncodeOperation.js +74 -58
  63. package/lib/encoder/EncodeOperation.js.map +1 -1
  64. package/lib/encoder/Encoder.d.ts +10 -8
  65. package/lib/encoder/Encoder.js +89 -56
  66. package/lib/encoder/Encoder.js.map +1 -1
  67. package/lib/encoder/Root.d.ts +17 -0
  68. package/lib/encoder/Root.js +44 -0
  69. package/lib/encoder/Root.js.map +1 -0
  70. package/lib/encoder/StateView.d.ts +2 -2
  71. package/lib/encoder/StateView.js +49 -59
  72. package/lib/encoder/StateView.js.map +1 -1
  73. package/lib/encoding/assert.d.ts +2 -1
  74. package/lib/encoding/assert.js +5 -5
  75. package/lib/encoding/assert.js.map +1 -1
  76. package/lib/encoding/decode.js +21 -22
  77. package/lib/encoding/decode.js.map +1 -1
  78. package/lib/encoding/encode.d.ts +2 -2
  79. package/lib/encoding/encode.js +40 -39
  80. package/lib/encoding/encode.js.map +1 -1
  81. package/lib/encoding/spec.d.ts +2 -1
  82. package/lib/encoding/spec.js +1 -0
  83. package/lib/encoding/spec.js.map +1 -1
  84. package/lib/index.d.ts +6 -3
  85. package/lib/index.js +18 -13
  86. package/lib/index.js.map +1 -1
  87. package/lib/types/TypeContext.d.ts +23 -0
  88. package/lib/types/TypeContext.js +102 -0
  89. package/lib/types/TypeContext.js.map +1 -0
  90. package/lib/types/custom/ArraySchema.d.ts +2 -2
  91. package/lib/types/custom/ArraySchema.js +6 -9
  92. package/lib/types/custom/ArraySchema.js.map +1 -1
  93. package/lib/types/custom/CollectionSchema.js +1 -0
  94. package/lib/types/custom/CollectionSchema.js.map +1 -1
  95. package/lib/types/custom/MapSchema.js +5 -0
  96. package/lib/types/custom/MapSchema.js.map +1 -1
  97. package/lib/types/custom/SetSchema.js +1 -0
  98. package/lib/types/custom/SetSchema.js.map +1 -1
  99. package/lib/types/registry.js +3 -4
  100. package/lib/types/registry.js.map +1 -1
  101. package/lib/types/symbols.d.ts +1 -0
  102. package/lib/types/symbols.js +2 -1
  103. package/lib/types/symbols.js.map +1 -1
  104. package/lib/types/utils.js +1 -2
  105. package/lib/types/utils.js.map +1 -1
  106. package/lib/utils.js +3 -4
  107. package/lib/utils.js.map +1 -1
  108. package/package.json +5 -5
  109. package/src/Metadata.ts +104 -26
  110. package/src/Reflection.ts +26 -28
  111. package/src/Schema.ts +35 -47
  112. package/src/annotations.ts +82 -176
  113. package/src/bench_encode.ts +121 -0
  114. package/src/debug.ts +56 -0
  115. package/src/decoder/DecodeOperation.ts +28 -11
  116. package/src/decoder/Decoder.ts +13 -11
  117. package/src/decoder/ReferenceTracker.ts +3 -2
  118. package/src/decoder/strategy/StateCallbacks.ts +152 -81
  119. package/src/encoder/ChangeTree.ts +147 -166
  120. package/src/encoder/EncodeOperation.ts +93 -70
  121. package/src/encoder/Encoder.ts +111 -65
  122. package/src/encoder/Root.ts +51 -0
  123. package/src/encoder/StateView.ts +53 -69
  124. package/src/encoding/assert.ts +4 -3
  125. package/src/encoding/decode.ts +1 -2
  126. package/src/encoding/encode.ts +25 -22
  127. package/src/encoding/spec.ts +1 -0
  128. package/src/index.ts +8 -14
  129. package/src/types/TypeContext.ts +122 -0
  130. package/src/types/custom/ArraySchema.ts +10 -2
  131. package/src/types/custom/CollectionSchema.ts +1 -0
  132. package/src/types/custom/MapSchema.ts +6 -0
  133. package/src/types/custom/SetSchema.ts +1 -0
  134. package/src/types/symbols.ts +2 -0
@@ -28,25 +28,23 @@ export class StateView {
28
28
  changes = new Map<ChangeTree, Map<number, OPERATION>>();
29
29
 
30
30
  // TODO: allow to set multiple tags at once
31
- add(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {
31
+ add(obj: Ref, tag: number = DEFAULT_VIEW_TAG, checkIncludeParent: boolean = true) {
32
32
  if (!obj[$changes]) {
33
33
  console.warn("StateView#add(), invalid object:", obj);
34
34
  return this;
35
35
  }
36
36
 
37
- let changeTree: ChangeTree = obj[$changes];
38
- this.items.add(changeTree);
39
-
40
- // Add children of this ChangeTree to this view
41
- changeTree.forEachChild((change, _) =>
42
- this.add(change.ref, tag));
43
-
44
- // FIXME: ArraySchema/MapSchema does not have metadata
37
+ // FIXME: ArraySchema/MapSchema do not have metadata
45
38
  const metadata: Metadata = obj.constructor[Symbol.metadata];
39
+ const changeTree: ChangeTree = obj[$changes];
40
+ this.items.add(changeTree);
46
41
 
47
- // add parent ChangeTree's, if they are invisible to this view
48
- // TODO: REFACTOR addParent()
49
- this.addParent(changeTree, tag);
42
+ // add parent ChangeTree's
43
+ // - if it was invisible to this view
44
+ // - if it were previously filtered out
45
+ if (checkIncludeParent && changeTree.parent) {
46
+ this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
47
+ }
50
48
 
51
49
  //
52
50
  // TODO: when adding an item of a MapSchema, the changes may not
@@ -72,8 +70,6 @@ export class StateView {
72
70
  }
73
71
  tags.add(tag);
74
72
 
75
- // console.log("BY TAG:", tag);
76
-
77
73
  // Ref: add tagged properties
78
74
  metadata?.[-3]?.[tag]?.forEach((index) => {
79
75
  if (changeTree.getChange(index) !== OPERATION.DELETE) {
@@ -82,89 +78,77 @@ export class StateView {
82
78
  });
83
79
 
84
80
  } else {
85
-
86
- // console.log("DEFAULT TAG", changeTree.allChanges);
87
-
88
- // // add default tag properties
89
- // metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
90
- // if (changeTree.getChange(index) !== OPERATION.DELETE) {
91
- // changes.set(index, OPERATION.ADD);
92
- // }
93
- // });
94
-
95
- const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
81
+ const isInvisible = this.invisible.has(changeTree);
82
+ const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
96
83
  ? changeTree.allFilteredChanges
97
84
  : changeTree.allChanges;
98
- const it = allChangesSet.keys();
99
- const isInvisible = this.invisible.has(changeTree);
100
85
 
101
- for (const index of it) {
86
+ changeSet.forEach((op, index) => {
87
+ const tagAtIndex = metadata?.[index].tag;
102
88
  if (
103
- (isInvisible || metadata?.[metadata?.[index]].tag === tag) &&
104
- changeTree.getChange(index) !== OPERATION.DELETE
89
+ (
90
+ isInvisible || // if "invisible", include all
91
+ tagAtIndex === undefined || // "all change" with no tag
92
+ tagAtIndex === tag // tagged property
93
+ ) &&
94
+ op !== OPERATION.DELETE
105
95
  ) {
106
- changes.set(index, OPERATION.ADD);
96
+ changes.set(index, op);
107
97
  }
108
- }
98
+ });
109
99
  }
110
100
 
111
- // TODO: avoid unnecessary iteration here
112
- while (
113
- changeTree.parent &&
114
- (changeTree = changeTree.parent[$changes]) &&
115
- (changeTree.isFiltered || changeTree.isPartiallyFiltered)
116
- ) {
117
- this.items.add(changeTree);
118
- }
101
+ // Add children of this ChangeTree to this view
102
+ changeTree.forEachChild((change, index) => {
103
+ // Do not ADD children that don't have the same tag
104
+ if (metadata && metadata[index].tag !== tag) {
105
+ return;
106
+ }
107
+ this.add(change.ref, tag, false);
108
+ });
119
109
 
120
110
  return this;
121
111
  }
122
112
 
123
- protected addParent(changeTree: ChangeTree, tag: number) {
124
- const parentRef = changeTree.parent;
125
- if (!parentRef) { return; }
113
+ protected addParent(changeTree: ChangeTree, parentIndex: number, tag: number) {
114
+ // view must have all "changeTree" parent tree
115
+ this.items.add(changeTree);
126
116
 
127
- const parentChangeTree = parentRef[$changes];
128
- const parentIndex = changeTree.parentIndex;
117
+ // add parent's parent
118
+ const parentChangeTree = changeTree.parent?.[$changes];
119
+ if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
120
+ this.addParent(parentChangeTree, changeTree.parentIndex, tag);
121
+ }
129
122
 
130
- if (!this.invisible.has(parentChangeTree)) {
131
- // parent is already available, no need to add it!
123
+ // parent is already available, no need to add it!
124
+ if (!this.invisible.has(changeTree)) {
132
125
  return;
133
126
  }
134
127
 
135
- this.addParent(parentChangeTree, tag);
136
-
137
128
  // add parent's tag properties
138
- if (parentChangeTree.getChange(parentIndex) !== OPERATION.DELETE) {
129
+ if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
139
130
 
140
- let parentChanges = this.changes.get(parentChangeTree);
141
- if (parentChanges === undefined) {
142
- parentChanges = new Map<number, OPERATION>();
143
- this.changes.set(parentChangeTree, parentChanges);
131
+ let changes = this.changes.get(changeTree);
132
+ if (changes === undefined) {
133
+ changes = new Map<number, OPERATION>();
134
+ this.changes.set(changeTree, changes);
144
135
  }
145
136
 
146
- // console.log("add parent change", {
147
- // parentIndex,
148
- // parentChanges,
149
- // parentChange: (
150
- // parentChangeTree.getChange(parentIndex) &&
151
- // OPERATION[parentChangeTree.getChange(parentIndex)]
152
- // ),
153
- // })
137
+ if (!this.tags) {
138
+ this.tags = new WeakMap<ChangeTree, Set<number>>();
139
+ }
154
140
 
155
- if (!this.tags) { this.tags = new WeakMap<ChangeTree, Set<number>>(); }
156
141
  let tags: Set<number>;
157
- if (!this.tags.has(parentChangeTree)) {
142
+ if (!this.tags.has(changeTree)) {
158
143
  tags = new Set<number>();
159
- this.tags.set(parentChangeTree, tags);
144
+ this.tags.set(changeTree, tags);
160
145
  } else {
161
- tags = this.tags.get(parentChangeTree);
146
+ tags = this.tags.get(changeTree);
162
147
  }
163
148
  tags.add(tag);
164
149
 
165
- parentChanges.set(parentIndex, OPERATION.ADD);
150
+ changes.set(parentIndex, OPERATION.ADD);
166
151
  }
167
-
168
152
  }
169
153
 
170
154
  remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {
@@ -182,7 +166,7 @@ export class StateView {
182
166
  let changes = this.changes.get(changeTree);
183
167
  if (changes === undefined) {
184
168
  changes = new Map<number, OPERATION>();
185
- this.changes.set(changeTree, changes)
169
+ this.changes.set(changeTree, changes);
186
170
  }
187
171
 
188
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
  }
@@ -32,8 +32,6 @@ import type { BufferLike } from "./encode";
32
32
  export interface Iterator { offset: number; }
33
33
 
34
34
  export function utf8Read(bytes: BufferLike, it: Iterator, length: number) {
35
- it.offset += length;
36
-
37
35
  var string = '', chr = 0;
38
36
  for (var i = it.offset, end = it.offset + length; i < end; i++) {
39
37
  var byte = bytes[i];
@@ -74,6 +72,7 @@ export function utf8Read(bytes: BufferLike, it: Iterator, length: number) {
74
72
  // (do not throw error to avoid server/client from crashing due to hack attemps)
75
73
  // throw new Error('Invalid byte ' + byte.toString(16));
76
74
  }
75
+ it.offset += length;
77
76
  return string;
78
77
  }
79
78
 
@@ -35,28 +35,32 @@ let textEncoder: TextEncoder;
35
35
  // @ts-ignore
36
36
  try { textEncoder = new TextEncoder(); } catch (e) { }
37
37
 
38
- export function utf8Length(str) {
39
- var c = 0, length = 0;
40
- for (var i = 0, l = str.length; i < l; i++) {
41
- c = str.charCodeAt(i);
42
- if (c < 0x80) {
43
- length += 1;
44
- }
45
- else if (c < 0x800) {
46
- length += 2;
47
- }
48
- else if (c < 0xd800 || c >= 0xe000) {
49
- length += 3;
50
- }
51
- else {
52
- i++;
53
- length += 4;
38
+ const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
39
+
40
+ export const utf8Length = (hasBufferByteLength)
41
+ ? Buffer.byteLength // node
42
+ : function (str: string, _?: any) {
43
+ var c = 0, length = 0;
44
+ for (var i = 0, l = str.length; i < l; i++) {
45
+ c = str.charCodeAt(i);
46
+ if (c < 0x80) {
47
+ length += 1;
48
+ }
49
+ else if (c < 0x800) {
50
+ length += 2;
51
+ }
52
+ else if (c < 0xd800 || c >= 0xe000) {
53
+ length += 3;
54
+ }
55
+ else {
56
+ i++;
57
+ length += 4;
58
+ }
59
+ }
60
+ return length;
54
61
  }
55
- }
56
- return length;
57
- }
58
62
 
59
- export function utf8Write(view, str, it) {
63
+ export function utf8Write(view: BufferLike, str: string, it: Iterator) {
60
64
  var c = 0;
61
65
  for (var i = 0, l = str.length; i < l; i++) {
62
66
  c = str.charCodeAt(i);
@@ -166,8 +170,7 @@ export function string(bytes: BufferLike, value: string, it: Iterator) {
166
170
  // encode `null` strings as empty.
167
171
  if (!value) { value = ""; }
168
172
 
169
- // let length = utf8Length(value);
170
- let length = Buffer.byteLength(value, "utf8");
173
+ let length = utf8Length(value, "utf8");
171
174
  let size = 0;
172
175
 
173
176
  // fixstr
@@ -25,5 +25,6 @@ export enum OPERATION {
25
25
  REVERSE = 15,
26
26
  MOVE = 32,
27
27
  DELETE_BY_REFID = 33, // This operation is only used at ENCODING time. During DECODING, DELETE_BY_REFID is converted to DELETE
28
+ ADD_BY_REFID = 129,
28
29
 
29
30
  }
package/src/index.ts CHANGED
@@ -1,8 +1,6 @@
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 };
3
+ export type { ToJSON } from "./types/HelperTypes";
6
4
 
7
5
  import { MapSchema } from "./types/custom/MapSchema"
8
6
  export { MapSchema };
@@ -28,6 +26,7 @@ registerType("collection", { constructor: CollectionSchema, });
28
26
  export { dumpChanges } from "./utils";
29
27
 
30
28
  // Encoder / Decoder
29
+ export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType } from "./types/symbols";
31
30
  export type { Iterator } from "./encoding/decode";
32
31
  import * as encode from "./encoding/encode";
33
32
  import * as decode from "./encoding/decode";
@@ -40,22 +39,17 @@ export {
40
39
  ReflectionField,
41
40
  } from "./Reflection";
42
41
 
42
+ // Annotations, Metadata and TypeContext
43
43
  export { Metadata } from "./Metadata";
44
-
45
- export {
46
- // Annotations
47
- type,
48
- deprecated,
49
- defineTypes,
50
- view,
51
-
52
- // Internals
53
- TypeContext,
54
- } from "./annotations";
44
+ export { type, deprecated, defineTypes, view, } from "./annotations";
45
+ export { TypeContext } from "./types/TypeContext";
55
46
 
56
47
  // Annotation types
57
48
  export type { DefinitionType, PrimitiveType, Definition, } from "./annotations";
58
49
 
50
+ export { getDecoderStateCallbacks, CallbackProxy, GetCallbackProxy } from "./decoder/strategy/StateCallbacks";
51
+ export { getRawChangesCallback } from "./decoder/strategy/RawChanges";
52
+
59
53
  export { Encoder } from "./encoder/Encoder";
60
54
  export { encodeSchemaOperation, encodeArray as encodeKeyValueOperation } from "./encoder/EncodeOperation";
61
55
  export { ChangeTree, Ref } from "./encoder/ChangeTree";
@@ -0,0 +1,122 @@
1
+ import { Metadata } from "../Metadata";
2
+ import { Schema } from "../Schema";
3
+
4
+ export class TypeContext {
5
+ types: { [id: number]: typeof Schema; } = {};
6
+ schemas = new Map<typeof Schema, number>();
7
+
8
+ hasFilters: boolean = false;
9
+ parentFiltered: {[typeIdAndParentIndex: string]: boolean} = {};
10
+
11
+ /**
12
+ * For inheritance support
13
+ * Keeps track of which classes extends which. (parent -> children)
14
+ */
15
+ static inheritedTypes = new Map<typeof Schema, Set<typeof Schema>>();
16
+
17
+ static register(target: typeof Schema) {
18
+ const parent = Object.getPrototypeOf(target);
19
+ if (parent !== Schema) {
20
+ let inherits = TypeContext.inheritedTypes.get(parent);
21
+ if (!inherits) {
22
+ inherits = new Set<typeof Schema>();
23
+ TypeContext.inheritedTypes.set(parent, inherits);
24
+ }
25
+ inherits.add(target);
26
+ }
27
+ }
28
+
29
+ constructor(rootClass?: typeof Schema) {
30
+ if (rootClass) {
31
+ this.discoverTypes(rootClass);
32
+ }
33
+ }
34
+
35
+ has(schema: typeof Schema) {
36
+ return this.schemas.has(schema);
37
+ }
38
+
39
+ get(typeid: number) {
40
+ return this.types[typeid];
41
+ }
42
+
43
+ add(schema: typeof Schema, typeid: number = this.schemas.size) {
44
+ // skip if already registered
45
+ if (this.schemas.has(schema)) {
46
+ return false;
47
+ }
48
+
49
+ this.types[typeid] = schema;
50
+
51
+ //
52
+ // Workaround to allow using an empty Schema (with no `@type()` fields)
53
+ //
54
+ if (schema[Symbol.metadata] === undefined) {
55
+ Metadata.init(schema);
56
+ }
57
+
58
+ this.schemas.set(schema, typeid);
59
+ return true;
60
+ }
61
+
62
+ getTypeId(klass: typeof Schema) {
63
+ return this.schemas.get(klass);
64
+ }
65
+
66
+ private discoverTypes(klass: typeof Schema, parentIndex?: number, parentFieldViewTag?: number) {
67
+ if (!this.add(klass)) {
68
+ return;
69
+ }
70
+
71
+ // add classes inherited from this base class
72
+ TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
73
+ this.discoverTypes(child, parentIndex, parentFieldViewTag);
74
+ });
75
+
76
+ const metadata: Metadata = (klass[Symbol.metadata] ??= {});
77
+
78
+ // if any schema/field has filters, mark "context" as having filters.
79
+ if (metadata[-2]) {
80
+ this.hasFilters = true;
81
+ }
82
+
83
+ if (parentFieldViewTag !== undefined) {
84
+ this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
85
+ }
86
+
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;
92
+
93
+ if (typeof (fieldType) === "string") {
94
+ continue;
95
+ }
96
+
97
+ if (Array.isArray(fieldType)) {
98
+ const type = fieldType[0];
99
+
100
+ // skip primitive types
101
+ if (type === "string") {
102
+ continue;
103
+ }
104
+
105
+ this.discoverTypes(type as typeof Schema, index, viewTag);
106
+
107
+ } else if (typeof (fieldType) === "function") {
108
+ this.discoverTypes(fieldType as typeof Schema, viewTag);
109
+
110
+ } else {
111
+ const type = Object.values(fieldType)[0];
112
+
113
+ // skip primitive types
114
+ if (typeof (type) === "string") {
115
+ continue;
116
+ }
117
+
118
+ this.discoverTypes(type as typeof Schema, index, viewTag);
119
+ }
120
+ }
121
+ }
122
+ }
@@ -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,11 +164,13 @@ 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];
165
173
  changeTree.indexedOperation(length, OPERATION.ADD, this.items.length);
166
- // changeTree.indexes[length] = length;
167
174
 
168
175
  this.items.push(value);
169
176
  this.tmpItems.push(value);
@@ -471,6 +478,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
471
478
  * @param thisArg An object to which the this keyword can refer in the callbackfn function.
472
479
  * If thisArg is omitted, undefined is used as the this value.
473
480
  */
481
+ every<S extends V>(predicate: (value: V, index: number, array: V[]) => value is S, thisArg?: any): this is S[];
474
482
  every(callbackfn: (value: V, index: number, array: V[]) => unknown, thisArg?: any): boolean {
475
483
  return this.items.every(callbackfn, thisArg);
476
484
  }
@@ -690,7 +698,7 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
690
698
  //
691
699
  // ES2023
692
700
  //
693
- with(index: number, value: V): V[] {
701
+ with(index: number, value: V): ArraySchema<V> {
694
702
  const copy = this.items.slice();
695
703
  copy[index] = value;
696
704
  return new ArraySchema(...copy);
@@ -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
  */