@colyseus/schema 3.0.0-alpha.4 → 3.0.0-alpha.40

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 (141) hide show
  1. package/README.md +131 -61
  2. package/bin/schema-debug +94 -0
  3. package/build/cjs/index.js +1521 -809
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/esm/index.mjs +1519 -808
  6. package/build/esm/index.mjs.map +1 -1
  7. package/build/umd/index.js +1528 -816
  8. package/lib/Metadata.d.ts +21 -9
  9. package/lib/Metadata.js +169 -32
  10. package/lib/Metadata.js.map +1 -1
  11. package/lib/Reflection.d.ts +19 -4
  12. package/lib/Reflection.js +66 -32
  13. package/lib/Reflection.js.map +1 -1
  14. package/lib/Schema.d.ts +4 -4
  15. package/lib/Schema.js +44 -50
  16. package/lib/Schema.js.map +1 -1
  17. package/lib/annotations.d.ts +31 -34
  18. package/lib/annotations.js +110 -160
  19. package/lib/annotations.js.map +1 -1
  20. package/lib/bench_encode.d.ts +1 -0
  21. package/lib/bench_encode.js +130 -0
  22. package/lib/bench_encode.js.map +1 -0
  23. package/lib/codegen/api.js +1 -2
  24. package/lib/codegen/api.js.map +1 -1
  25. package/lib/codegen/languages/cpp.js +1 -2
  26. package/lib/codegen/languages/cpp.js.map +1 -1
  27. package/lib/codegen/languages/csharp.js +1 -2
  28. package/lib/codegen/languages/csharp.js.map +1 -1
  29. package/lib/codegen/languages/haxe.js +1 -2
  30. package/lib/codegen/languages/haxe.js.map +1 -1
  31. package/lib/codegen/languages/java.js +1 -2
  32. package/lib/codegen/languages/java.js.map +1 -1
  33. package/lib/codegen/languages/js.js +1 -2
  34. package/lib/codegen/languages/js.js.map +1 -1
  35. package/lib/codegen/languages/lua.js +1 -2
  36. package/lib/codegen/languages/lua.js.map +1 -1
  37. package/lib/codegen/languages/ts.js +1 -2
  38. package/lib/codegen/languages/ts.js.map +1 -1
  39. package/lib/codegen/parser.js +85 -3
  40. package/lib/codegen/parser.js.map +1 -1
  41. package/lib/codegen/types.js +6 -3
  42. package/lib/codegen/types.js.map +1 -1
  43. package/lib/debug.d.ts +1 -0
  44. package/lib/debug.js +51 -0
  45. package/lib/debug.js.map +1 -0
  46. package/lib/decoder/DecodeOperation.d.ts +0 -1
  47. package/lib/decoder/DecodeOperation.js +30 -12
  48. package/lib/decoder/DecodeOperation.js.map +1 -1
  49. package/lib/decoder/Decoder.d.ts +6 -7
  50. package/lib/decoder/Decoder.js +9 -9
  51. package/lib/decoder/Decoder.js.map +1 -1
  52. package/lib/decoder/ReferenceTracker.js +3 -2
  53. package/lib/decoder/ReferenceTracker.js.map +1 -1
  54. package/lib/decoder/strategy/RawChanges.js +1 -2
  55. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  56. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  57. package/lib/decoder/strategy/StateCallbacks.js +75 -65
  58. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  59. package/lib/encoder/ChangeTree.d.ts +27 -21
  60. package/lib/encoder/ChangeTree.js +246 -186
  61. package/lib/encoder/ChangeTree.js.map +1 -1
  62. package/lib/encoder/EncodeOperation.d.ts +3 -6
  63. package/lib/encoder/EncodeOperation.js +47 -61
  64. package/lib/encoder/EncodeOperation.js.map +1 -1
  65. package/lib/encoder/Encoder.d.ts +9 -8
  66. package/lib/encoder/Encoder.js +165 -88
  67. package/lib/encoder/Encoder.js.map +1 -1
  68. package/lib/encoder/Root.d.ts +22 -0
  69. package/lib/encoder/Root.js +81 -0
  70. package/lib/encoder/Root.js.map +1 -0
  71. package/lib/encoder/StateView.d.ts +7 -7
  72. package/lib/encoder/StateView.js +70 -74
  73. package/lib/encoder/StateView.js.map +1 -1
  74. package/lib/encoding/assert.d.ts +2 -1
  75. package/lib/encoding/assert.js +5 -5
  76. package/lib/encoding/assert.js.map +1 -1
  77. package/lib/encoding/decode.js +20 -21
  78. package/lib/encoding/decode.js.map +1 -1
  79. package/lib/encoding/encode.d.ts +2 -2
  80. package/lib/encoding/encode.js +52 -48
  81. package/lib/encoding/encode.js.map +1 -1
  82. package/lib/encoding/spec.d.ts +2 -1
  83. package/lib/encoding/spec.js +1 -0
  84. package/lib/encoding/spec.js.map +1 -1
  85. package/lib/index.d.ts +6 -3
  86. package/lib/index.js +19 -13
  87. package/lib/index.js.map +1 -1
  88. package/lib/types/HelperTypes.d.ts +34 -2
  89. package/lib/types/HelperTypes.js.map +1 -1
  90. package/lib/types/TypeContext.d.ts +23 -0
  91. package/lib/types/TypeContext.js +111 -0
  92. package/lib/types/TypeContext.js.map +1 -0
  93. package/lib/types/custom/ArraySchema.d.ts +2 -2
  94. package/lib/types/custom/ArraySchema.js +33 -22
  95. package/lib/types/custom/ArraySchema.js.map +1 -1
  96. package/lib/types/custom/CollectionSchema.js +1 -0
  97. package/lib/types/custom/CollectionSchema.js.map +1 -1
  98. package/lib/types/custom/MapSchema.d.ts +3 -1
  99. package/lib/types/custom/MapSchema.js +12 -4
  100. package/lib/types/custom/MapSchema.js.map +1 -1
  101. package/lib/types/custom/SetSchema.js +1 -0
  102. package/lib/types/custom/SetSchema.js.map +1 -1
  103. package/lib/types/registry.js +3 -4
  104. package/lib/types/registry.js.map +1 -1
  105. package/lib/types/symbols.d.ts +8 -5
  106. package/lib/types/symbols.js +9 -6
  107. package/lib/types/symbols.js.map +1 -1
  108. package/lib/types/utils.js +1 -2
  109. package/lib/types/utils.js.map +1 -1
  110. package/lib/utils.js +9 -7
  111. package/lib/utils.js.map +1 -1
  112. package/package.json +7 -6
  113. package/src/Metadata.ts +190 -42
  114. package/src/Reflection.ts +77 -39
  115. package/src/Schema.ts +59 -64
  116. package/src/annotations.ts +155 -203
  117. package/src/bench_encode.ts +108 -0
  118. package/src/codegen/parser.ts +107 -0
  119. package/src/codegen/types.ts +1 -0
  120. package/src/debug.ts +55 -0
  121. package/src/decoder/DecodeOperation.ts +40 -12
  122. package/src/decoder/Decoder.ts +14 -12
  123. package/src/decoder/ReferenceTracker.ts +3 -2
  124. package/src/decoder/strategy/StateCallbacks.ts +153 -82
  125. package/src/encoder/ChangeTree.ts +286 -202
  126. package/src/encoder/EncodeOperation.ts +77 -77
  127. package/src/encoder/Encoder.ts +201 -96
  128. package/src/encoder/Root.ts +93 -0
  129. package/src/encoder/StateView.ts +76 -88
  130. package/src/encoding/assert.ts +4 -3
  131. package/src/encoding/encode.ts +37 -31
  132. package/src/encoding/spec.ts +1 -0
  133. package/src/index.ts +8 -14
  134. package/src/types/HelperTypes.ts +54 -2
  135. package/src/types/TypeContext.ts +133 -0
  136. package/src/types/custom/ArraySchema.ts +49 -19
  137. package/src/types/custom/CollectionSchema.ts +1 -0
  138. package/src/types/custom/MapSchema.ts +18 -5
  139. package/src/types/custom/SetSchema.ts +1 -0
  140. package/src/types/symbols.ts +10 -7
  141. package/src/utils.ts +7 -3
@@ -1,11 +1,11 @@
1
- import { ChangeTree, Ref } from "./ChangeTree";
2
- import { $changes } from "../types/symbols";
1
+ import { ChangeSet, ChangeTree, IndexedOperations, Ref } from "./ChangeTree";
2
+ import { $changes, $fieldIndexesByViewTag, $viewFieldIndexes } from "../types/symbols";
3
3
  import { DEFAULT_VIEW_TAG } from "../annotations";
4
4
  import { OPERATION } from "../encoding/spec";
5
5
  import { Metadata } from "../Metadata";
6
- import type { Schema } from "../Schema";
7
6
 
8
- export function createView(root: Schema) {
7
+ export function createView() {
8
+ return new StateView();
9
9
  }
10
10
 
11
11
  export class StateView {
@@ -25,37 +25,35 @@ export class StateView {
25
25
  * Manual "ADD" operations for changes per ChangeTree, specific to this view.
26
26
  * (This is used to force encoding a property, even if it was not changed)
27
27
  */
28
- changes = new Map<ChangeTree, Map<number, OPERATION>>();
28
+ changes: { [refId: number]: IndexedOperations } = {};
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
53
51
  // be set (only the parent's changes are set)
54
52
  //
55
- let changes = this.changes.get(changeTree);
53
+ let changes = this.changes[changeTree.refId];
56
54
  if (changes === undefined) {
57
- changes = new Map<number, OPERATION>();
58
- this.changes.set(changeTree, changes)
55
+ changes = {};
56
+ this.changes[changeTree.refId] = changes;
59
57
  }
60
58
 
61
59
  // set tag
@@ -72,99 +70,89 @@ 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
- metadata?.[-3]?.[tag]?.forEach((index) => {
74
+ metadata?.[$fieldIndexesByViewTag]?.[tag]?.forEach((index) => {
79
75
  if (changeTree.getChange(index) !== OPERATION.DELETE) {
80
- changes.set(index, OPERATION.ADD)
76
+ changes[index] = OPERATION.ADD;
81
77
  }
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
+ for (let i = 0, len = changeSet.operations.length; i < len; i++) {
87
+ const index = changeSet.operations[i];
88
+ if (index === undefined) { continue; } // skip "undefined" indexes
89
+
90
+ const op = changeTree.indexedOperations[index];
91
+ const tagAtIndex = metadata?.[index].tag;
102
92
  if (
103
- (isInvisible || metadata?.[metadata?.[index]].tag === tag) &&
104
- changeTree.getChange(index) !== OPERATION.DELETE
93
+ (
94
+ isInvisible || // if "invisible", include all
95
+ tagAtIndex === undefined || // "all change" with no tag
96
+ tagAtIndex === tag // tagged property
97
+ ) &&
98
+ op !== OPERATION.DELETE
105
99
  ) {
106
- changes.set(index, OPERATION.ADD);
100
+ changes[index] = op;
107
101
  }
108
102
  }
109
103
  }
110
104
 
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
- }
105
+ // Add children of this ChangeTree to this view
106
+ changeTree.forEachChild((change, index) => {
107
+ // Do not ADD children that don't have the same tag
108
+ if (metadata && metadata[index].tag !== tag) {
109
+ return;
110
+ }
111
+ this.add(change.ref, tag, false);
112
+ });
119
113
 
120
114
  return this;
121
115
  }
122
116
 
123
- protected addParent(changeTree: ChangeTree, tag: number) {
124
- const parentRef = changeTree.parent;
125
- if (!parentRef) { return; }
117
+ protected addParent(changeTree: ChangeTree, parentIndex: number, tag: number) {
118
+ // view must have all "changeTree" parent tree
119
+ this.items.add(changeTree);
126
120
 
127
- const parentChangeTree = parentRef[$changes];
128
- const parentIndex = changeTree.parentIndex;
121
+ // add parent's parent
122
+ const parentChangeTree = changeTree.parent?.[$changes];
123
+ if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
124
+ this.addParent(parentChangeTree, changeTree.parentIndex, tag);
125
+ }
129
126
 
130
- if (!this.invisible.has(parentChangeTree)) {
131
- // parent is already available, no need to add it!
127
+ // parent is already available, no need to add it!
128
+ if (!this.invisible.has(changeTree)) {
132
129
  return;
133
130
  }
134
131
 
135
- this.addParent(parentChangeTree, tag);
136
-
137
132
  // add parent's tag properties
138
- if (parentChangeTree.getChange(parentIndex) !== OPERATION.DELETE) {
133
+ if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
139
134
 
140
- let parentChanges = this.changes.get(parentChangeTree);
141
- if (parentChanges === undefined) {
142
- parentChanges = new Map<number, OPERATION>();
143
- this.changes.set(parentChangeTree, parentChanges);
135
+ let changes = this.changes[changeTree.refId];
136
+ if (changes === undefined) {
137
+ changes = {};
138
+ this.changes[changeTree.refId] = changes;
144
139
  }
145
140
 
146
- // console.log("add parent change", {
147
- // parentIndex,
148
- // parentChanges,
149
- // parentChange: (
150
- // parentChangeTree.getChange(parentIndex) &&
151
- // OPERATION[parentChangeTree.getChange(parentIndex)]
152
- // ),
153
- // })
141
+ if (!this.tags) {
142
+ this.tags = new WeakMap<ChangeTree, Set<number>>();
143
+ }
154
144
 
155
- if (!this.tags) { this.tags = new WeakMap<ChangeTree, Set<number>>(); }
156
145
  let tags: Set<number>;
157
- if (!this.tags.has(parentChangeTree)) {
146
+ if (!this.tags.has(changeTree)) {
158
147
  tags = new Set<number>();
159
- this.tags.set(parentChangeTree, tags);
148
+ this.tags.set(changeTree, tags);
160
149
  } else {
161
- tags = this.tags.get(parentChangeTree);
150
+ tags = this.tags.get(changeTree);
162
151
  }
163
152
  tags.add(tag);
164
153
 
165
- parentChanges.set(parentIndex, OPERATION.ADD);
154
+ changes[parentIndex] = OPERATION.ADD;
166
155
  }
167
-
168
156
  }
169
157
 
170
158
  remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {
@@ -179,10 +167,10 @@ export class StateView {
179
167
  const ref = changeTree.ref;
180
168
  const metadata: Metadata = ref.constructor[Symbol.metadata];
181
169
 
182
- let changes = this.changes.get(changeTree);
170
+ let changes = this.changes[changeTree.refId];
183
171
  if (changes === undefined) {
184
- changes = new Map<number, OPERATION>();
185
- this.changes.set(changeTree, changes)
172
+ changes = {};
173
+ this.changes[changeTree.refId] = changes;
186
174
  }
187
175
 
188
176
  if (tag === DEFAULT_VIEW_TAG) {
@@ -190,25 +178,25 @@ export class StateView {
190
178
  const parent = changeTree.parent;
191
179
  if (!Metadata.isValidInstance(parent)) {
192
180
  const parentChangeTree = parent[$changes];
193
- let changes = this.changes.get(parentChangeTree);
181
+ let changes = this.changes[parentChangeTree.refId];
194
182
  if (changes === undefined) {
195
- changes = new Map<number, OPERATION>();
196
- this.changes.set(parentChangeTree, changes)
183
+ changes = {};
184
+ this.changes[parentChangeTree.refId] = changes;
197
185
  }
198
186
  // DELETE / DELETE BY REF ID
199
- changes.set(changeTree.parentIndex, OPERATION.DELETE);
187
+ changes[changeTree.parentIndex] = OPERATION.DELETE;
200
188
 
201
189
  } else {
202
190
  // delete all "tagged" properties.
203
- metadata[-2].forEach((index) =>
204
- changes.set(index, OPERATION.DELETE));
191
+ metadata[$viewFieldIndexes].forEach((index) =>
192
+ changes[index] = OPERATION.DELETE);
205
193
  }
206
194
 
207
195
 
208
196
  } else {
209
197
  // delete only tagged properties
210
- metadata[-3][tag].forEach((index) =>
211
- changes.set(index, OPERATION.DELETE));
198
+ metadata[$fieldIndexesByViewTag][tag].forEach((index) =>
199
+ changes[index] = OPERATION.DELETE);
212
200
  }
213
201
 
214
202
  // remove 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
  }
@@ -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: (str: string, _?: any) => number = (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);
@@ -64,21 +68,24 @@ export function utf8Write(view, str, it) {
64
68
  view[it.offset++] = c;
65
69
  }
66
70
  else if (c < 0x800) {
67
- view[it.offset++] = 0xc0 | (c >> 6);
68
- view[it.offset++] = 0x80 | (c & 0x3f);
71
+ view[it.offset] = 0xc0 | (c >> 6);
72
+ view[it.offset + 1] = 0x80 | (c & 0x3f);
73
+ it.offset += 2;
69
74
  }
70
75
  else if (c < 0xd800 || c >= 0xe000) {
71
- view[it.offset++] = 0xe0 | (c >> 12);
72
- view[it.offset++] = 0x80 | (c >> 6 & 0x3f);
73
- view[it.offset++] = 0x80 | (c & 0x3f);
76
+ view[it.offset] = 0xe0 | (c >> 12);
77
+ view[it.offset+1] = 0x80 | (c >> 6 & 0x3f);
78
+ view[it.offset+2] = 0x80 | (c & 0x3f);
79
+ it.offset += 3;
74
80
  }
75
81
  else {
76
82
  i++;
77
83
  c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
78
- view[it.offset++] = 0xf0 | (c >> 18);
79
- view[it.offset++] = 0x80 | (c >> 12 & 0x3f);
80
- view[it.offset++] = 0x80 | (c >> 6 & 0x3f);
81
- view[it.offset++] = 0x80 | (c & 0x3f);
84
+ view[it.offset] = 0xf0 | (c >> 18);
85
+ view[it.offset+1] = 0x80 | (c >> 12 & 0x3f);
86
+ view[it.offset+2] = 0x80 | (c >> 6 & 0x3f);
87
+ view[it.offset+3] = 0x80 | (c & 0x3f);
88
+ it.offset += 4;
82
89
  }
83
90
  }
84
91
  }
@@ -166,8 +173,7 @@ export function string(bytes: BufferLike, value: string, it: Iterator) {
166
173
  // encode `null` strings as empty.
167
174
  if (!value) { value = ""; }
168
175
 
169
- // let length = utf8Length(value);
170
- let length = Buffer.byteLength(value, "utf8");
176
+ let length = utf8Length(value, "utf8");
171
177
  let size = 0;
172
178
 
173
179
  // 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, schema, type SchemaWithExtends, } 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";
@@ -1,5 +1,11 @@
1
- import { ArraySchema } from "./custom/ArraySchema";
2
- import { MapSchema } from "./custom/MapSchema";
1
+ import type { Definition, DefinitionType, PrimitiveType, RawPrimitiveType } from "../annotations";
2
+ import type { Schema } from "../Schema";
3
+ import type { ArraySchema } from "./custom/ArraySchema";
4
+ import type { CollectionSchema } from "./custom/CollectionSchema";
5
+ import type { MapSchema } from "./custom/MapSchema";
6
+ import type { SetSchema } from "./custom/SetSchema";
7
+
8
+ export type Constructor<T = {}> = new (...args: any[]) => T;
3
9
 
4
10
  export interface Collection<K = any, V = any, IT = V> {
5
11
  [Symbol.iterator](): IterableIterator<IT>;
@@ -7,6 +13,52 @@ export interface Collection<K = any, V = any, IT = V> {
7
13
  entries(): IterableIterator<[K, V]>;
8
14
  }
9
15
 
16
+ export type InferValueType<T extends DefinitionType> =
17
+ T extends "string" ? string
18
+ : T extends "number" ? number
19
+ : T extends "int8" ? number
20
+ : T extends "uint8" ? number
21
+ : T extends "int16" ? number
22
+ : T extends "uint16" ? number
23
+ : T extends "int32" ? number
24
+ : T extends "uint32" ? number
25
+ : T extends "int64" ? number
26
+ : T extends "uint64" ? number
27
+ : T extends "float32" ? number
28
+ : T extends "float64" ? number
29
+ : T extends "boolean" ? boolean
30
+
31
+ : T extends { type: infer ChildType extends Constructor } ? InstanceType<ChildType>
32
+ : T extends { type: infer ChildType extends PrimitiveType } ? ChildType
33
+
34
+ : T extends Array<infer ChildType extends Constructor> ? InstanceType<ChildType>[]
35
+ : T extends Array<infer ChildType extends RawPrimitiveType> ? ChildType[]
36
+
37
+ : T extends { array: infer ChildType extends Constructor } ? InstanceType<ChildType>[]
38
+ : T extends { array: infer ChildType extends PrimitiveType } ? ChildType[]
39
+
40
+ : T extends { map: infer ChildType extends Constructor } ? MapSchema<InstanceType<ChildType>>
41
+ : T extends { map: infer ChildType extends PrimitiveType } ? MapSchema<ChildType>
42
+
43
+ : T extends { set: infer ChildType extends Constructor } ? SetSchema<InstanceType<ChildType>>
44
+ : T extends { set: infer ChildType extends PrimitiveType } ? SetSchema<ChildType>
45
+
46
+ : T extends { collection: infer ChildType extends Constructor } ? CollectionSchema<InstanceType<ChildType>>
47
+ : T extends { collection: infer ChildType extends PrimitiveType } ? CollectionSchema<ChildType>
48
+
49
+ : T extends Constructor ? InstanceType<T>
50
+ : T extends PrimitiveType ? T
51
+
52
+ : never;
53
+
54
+ export type InferSchemaInstanceType<T extends Definition> = {
55
+ [K in keyof T]: InferValueType<T[K]>
56
+ } & Schema;
57
+
58
+ export type DefinedSchemaType<T extends Definition, P extends typeof Schema> = {
59
+ new (): InferSchemaInstanceType<T> & InstanceType<P>;
60
+ } & typeof Schema;
61
+
10
62
  export type NonFunctionProps<T> = Omit<T, {
11
63
  [K in keyof T]: T[K] extends Function ? K : never;
12
64
  }[keyof T]>;
@@ -0,0 +1,133 @@
1
+ import { Metadata } from "../Metadata";
2
+ import { Schema } from "../Schema";
3
+ import { $viewFieldIndexes } from "./symbols";
4
+
5
+ export class TypeContext {
6
+ types: { [id: number]: typeof Schema; } = {};
7
+ schemas = new Map<typeof Schema, number>();
8
+
9
+ hasFilters: boolean = false;
10
+ parentFiltered: {[typeIdAndParentIndex: string]: boolean} = {};
11
+
12
+ /**
13
+ * For inheritance support
14
+ * Keeps track of which classes extends which. (parent -> children)
15
+ */
16
+ static inheritedTypes = new Map<typeof Schema, Set<typeof Schema>>();
17
+
18
+ static register(target: typeof Schema) {
19
+ const parent = Object.getPrototypeOf(target);
20
+ if (parent !== Schema) {
21
+ let inherits = TypeContext.inheritedTypes.get(parent);
22
+ if (!inherits) {
23
+ inherits = new Set<typeof Schema>();
24
+ TypeContext.inheritedTypes.set(parent, inherits);
25
+ }
26
+ inherits.add(target);
27
+ }
28
+ }
29
+
30
+ constructor(rootClass?: typeof Schema) {
31
+ if (rootClass) {
32
+ this.discoverTypes(rootClass);
33
+ }
34
+ }
35
+
36
+ has(schema: typeof Schema) {
37
+ return this.schemas.has(schema);
38
+ }
39
+
40
+ get(typeid: number) {
41
+ return this.types[typeid];
42
+ }
43
+
44
+ add(schema: typeof Schema, typeid: number = this.schemas.size) {
45
+ // skip if already registered
46
+ if (this.schemas.has(schema)) {
47
+ return false;
48
+ }
49
+
50
+ this.types[typeid] = schema;
51
+
52
+ //
53
+ // Workaround to allow using an empty Schema (with no `@type()` fields)
54
+ //
55
+ if (schema[Symbol.metadata] === undefined) {
56
+ Metadata.initialize(schema);
57
+ }
58
+
59
+ this.schemas.set(schema, typeid);
60
+ return true;
61
+ }
62
+
63
+ getTypeId(klass: typeof Schema) {
64
+ return this.schemas.get(klass);
65
+ }
66
+
67
+ private discoverTypes(klass: typeof Schema, parentIndex?: number, parentFieldViewTag?: number) {
68
+ if (!this.add(klass)) {
69
+ return;
70
+ }
71
+
72
+ // add classes inherited from this base class
73
+ TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
74
+ this.discoverTypes(child, parentIndex, parentFieldViewTag);
75
+ });
76
+
77
+ // add parent classes
78
+ let parent: any = klass;
79
+ while (
80
+ (parent = Object.getPrototypeOf(parent)) &&
81
+ parent !== Schema && // stop at root (Schema)
82
+ parent !== Function.prototype // stop at root (non-Schema)
83
+ ) {
84
+ this.discoverTypes(parent);
85
+ }
86
+
87
+ const metadata: Metadata = (klass[Symbol.metadata] ??= {});
88
+
89
+ // if any schema/field has filters, mark "context" as having filters.
90
+ if (metadata[$viewFieldIndexes]) {
91
+ this.hasFilters = true;
92
+ }
93
+
94
+ if (parentFieldViewTag !== undefined) {
95
+ this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
96
+ }
97
+
98
+ for (const fieldIndex in metadata) {
99
+ const index = fieldIndex as any as number;
100
+
101
+ const fieldType = metadata[index].type;
102
+ const viewTag = metadata[index].tag;
103
+
104
+ if (typeof (fieldType) === "string") {
105
+ continue;
106
+ }
107
+
108
+ if (Array.isArray(fieldType)) {
109
+ const type = fieldType[0];
110
+
111
+ // skip primitive types
112
+ if (type === "string") {
113
+ continue;
114
+ }
115
+
116
+ this.discoverTypes(type as typeof Schema, index, viewTag);
117
+
118
+ } else if (typeof (fieldType) === "function") {
119
+ this.discoverTypes(fieldType as typeof Schema, viewTag);
120
+
121
+ } else {
122
+ const type = Object.values(fieldType)[0];
123
+
124
+ // skip primitive types
125
+ if (typeof (type) === "string") {
126
+ continue;
127
+ }
128
+
129
+ this.discoverTypes(type as typeof Schema, index, viewTag);
130
+ }
131
+ }
132
+ }
133
+ }