@colyseus/schema 3.0.0-alpha.9 → 3.0.0

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 (150) hide show
  1. package/README.md +148 -62
  2. package/bin/schema-debug +94 -0
  3. package/build/cjs/index.js +2222 -1513
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/esm/index.mjs +2223 -1516
  6. package/build/esm/index.mjs.map +1 -1
  7. package/build/umd/index.js +2225 -1516
  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 -31
  13. package/lib/Reflection.js.map +1 -1
  14. package/lib/Schema.d.ts +12 -5
  15. package/lib/Schema.js +57 -56
  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 +9 -46
  28. package/lib/codegen/languages/csharp.js.map +1 -1
  29. package/lib/codegen/languages/haxe.js +4 -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 +23 -25
  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 +3 -4
  47. package/lib/decoder/DecodeOperation.js +35 -17
  48. package/lib/decoder/DecodeOperation.js.map +1 -1
  49. package/lib/decoder/Decoder.d.ts +5 -6
  50. package/lib/decoder/Decoder.js +10 -10
  51. package/lib/decoder/Decoder.js.map +1 -1
  52. package/lib/decoder/ReferenceTracker.js +4 -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 +74 -64
  58. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  59. package/lib/encoder/ChangeTree.d.ts +28 -20
  60. package/lib/encoder/ChangeTree.js +242 -188
  61. package/lib/encoder/ChangeTree.js.map +1 -1
  62. package/lib/encoder/EncodeOperation.d.ts +3 -6
  63. package/lib/encoder/EncodeOperation.js +51 -65
  64. package/lib/encoder/EncodeOperation.js.map +1 -1
  65. package/lib/encoder/Encoder.d.ts +8 -7
  66. package/lib/encoder/Encoder.js +128 -79
  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 +72 -74
  73. package/lib/encoder/StateView.js.map +1 -1
  74. package/lib/encoding/assert.d.ts +7 -6
  75. package/lib/encoding/assert.js +13 -5
  76. package/lib/encoding/assert.js.map +1 -1
  77. package/lib/encoding/decode.d.ts +36 -19
  78. package/lib/encoding/decode.js +54 -84
  79. package/lib/encoding/decode.js.map +1 -1
  80. package/lib/encoding/encode.d.ts +36 -18
  81. package/lib/encoding/encode.js +61 -48
  82. package/lib/encoding/encode.js.map +1 -1
  83. package/lib/encoding/spec.d.ts +4 -5
  84. package/lib/encoding/spec.js +1 -2
  85. package/lib/encoding/spec.js.map +1 -1
  86. package/lib/index.d.ts +10 -9
  87. package/lib/index.js +24 -17
  88. package/lib/index.js.map +1 -1
  89. package/lib/types/HelperTypes.d.ts +34 -2
  90. package/lib/types/HelperTypes.js.map +1 -1
  91. package/lib/types/TypeContext.d.ts +29 -0
  92. package/lib/types/TypeContext.js +151 -0
  93. package/lib/types/TypeContext.js.map +1 -0
  94. package/lib/types/custom/ArraySchema.d.ts +2 -2
  95. package/lib/types/custom/ArraySchema.js +33 -22
  96. package/lib/types/custom/ArraySchema.js.map +1 -1
  97. package/lib/types/custom/CollectionSchema.d.ts +2 -2
  98. package/lib/types/custom/CollectionSchema.js +1 -0
  99. package/lib/types/custom/CollectionSchema.js.map +1 -1
  100. package/lib/types/custom/MapSchema.d.ts +18 -16
  101. package/lib/types/custom/MapSchema.js +12 -4
  102. package/lib/types/custom/MapSchema.js.map +1 -1
  103. package/lib/types/custom/SetSchema.d.ts +2 -2
  104. package/lib/types/custom/SetSchema.js +1 -0
  105. package/lib/types/custom/SetSchema.js.map +1 -1
  106. package/lib/types/registry.d.ts +8 -1
  107. package/lib/types/registry.js +23 -6
  108. package/lib/types/registry.js.map +1 -1
  109. package/lib/types/symbols.d.ts +8 -5
  110. package/lib/types/symbols.js +9 -6
  111. package/lib/types/symbols.js.map +1 -1
  112. package/lib/types/utils.js +1 -2
  113. package/lib/types/utils.js.map +1 -1
  114. package/lib/utils.js +9 -7
  115. package/lib/utils.js.map +1 -1
  116. package/package.json +19 -18
  117. package/src/Metadata.ts +190 -42
  118. package/src/Reflection.ts +76 -38
  119. package/src/Schema.ts +72 -70
  120. package/src/annotations.ts +156 -202
  121. package/src/bench_encode.ts +108 -0
  122. package/src/codegen/languages/csharp.ts +8 -47
  123. package/src/codegen/languages/haxe.ts +4 -0
  124. package/src/codegen/languages/lua.ts +19 -27
  125. package/src/codegen/parser.ts +107 -0
  126. package/src/codegen/types.ts +1 -0
  127. package/src/debug.ts +55 -0
  128. package/src/decoder/DecodeOperation.ts +43 -15
  129. package/src/decoder/Decoder.ts +12 -10
  130. package/src/decoder/ReferenceTracker.ts +5 -3
  131. package/src/decoder/strategy/StateCallbacks.ts +152 -81
  132. package/src/encoder/ChangeTree.ts +282 -209
  133. package/src/encoder/EncodeOperation.ts +78 -78
  134. package/src/encoder/Encoder.ts +152 -87
  135. package/src/encoder/Root.ts +93 -0
  136. package/src/encoder/StateView.ts +80 -88
  137. package/src/encoding/assert.ts +17 -8
  138. package/src/encoding/decode.ts +73 -93
  139. package/src/encoding/encode.ts +76 -45
  140. package/src/encoding/spec.ts +3 -5
  141. package/src/index.ts +12 -20
  142. package/src/types/HelperTypes.ts +54 -2
  143. package/src/types/TypeContext.ts +175 -0
  144. package/src/types/custom/ArraySchema.ts +49 -19
  145. package/src/types/custom/CollectionSchema.ts +1 -0
  146. package/src/types/custom/MapSchema.ts +30 -17
  147. package/src/types/custom/SetSchema.ts +1 -0
  148. package/src/types/registry.ts +22 -3
  149. package/src/types/symbols.ts +10 -7
  150. package/src/utils.ts +7 -3
@@ -35,9 +35,17 @@ let textEncoder: TextEncoder;
35
35
  // @ts-ignore
36
36
  try { textEncoder = new TextEncoder(); } catch (e) { }
37
37
 
38
+ // force little endian to facilitate decoding on multiple implementations
39
+ const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;
40
+ const _convoBuffer = new ArrayBuffer(8);
41
+ const _int32 = new Int32Array(_convoBuffer);
42
+ const _float32 = new Float32Array(_convoBuffer);
43
+ const _float64 = new Float64Array(_convoBuffer);
44
+ const _int64 = new BigInt64Array(_convoBuffer);
45
+
38
46
  const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
39
47
 
40
- export const utf8Length = (hasBufferByteLength)
48
+ const utf8Length: (str: string, _?: any) => number = (hasBufferByteLength)
41
49
  ? Buffer.byteLength // node
42
50
  : function (str: string, _?: any) {
43
51
  var c = 0, length = 0;
@@ -60,7 +68,7 @@ export const utf8Length = (hasBufferByteLength)
60
68
  return length;
61
69
  }
62
70
 
63
- export function utf8Write(view: BufferLike, str: string, it: Iterator) {
71
+ function utf8Write(view: BufferLike, str: string, it: Iterator) {
64
72
  var c = 0;
65
73
  for (var i = 0, l = str.length; i < l; i++) {
66
74
  c = str.charCodeAt(i);
@@ -68,51 +76,54 @@ export function utf8Write(view: BufferLike, str: string, it: Iterator) {
68
76
  view[it.offset++] = c;
69
77
  }
70
78
  else if (c < 0x800) {
71
- view[it.offset++] = 0xc0 | (c >> 6);
72
- view[it.offset++] = 0x80 | (c & 0x3f);
79
+ view[it.offset] = 0xc0 | (c >> 6);
80
+ view[it.offset + 1] = 0x80 | (c & 0x3f);
81
+ it.offset += 2;
73
82
  }
74
83
  else if (c < 0xd800 || c >= 0xe000) {
75
- view[it.offset++] = 0xe0 | (c >> 12);
76
- view[it.offset++] = 0x80 | (c >> 6 & 0x3f);
77
- view[it.offset++] = 0x80 | (c & 0x3f);
84
+ view[it.offset] = 0xe0 | (c >> 12);
85
+ view[it.offset+1] = 0x80 | (c >> 6 & 0x3f);
86
+ view[it.offset+2] = 0x80 | (c & 0x3f);
87
+ it.offset += 3;
78
88
  }
79
89
  else {
80
90
  i++;
81
91
  c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
82
- view[it.offset++] = 0xf0 | (c >> 18);
83
- view[it.offset++] = 0x80 | (c >> 12 & 0x3f);
84
- view[it.offset++] = 0x80 | (c >> 6 & 0x3f);
85
- view[it.offset++] = 0x80 | (c & 0x3f);
92
+ view[it.offset] = 0xf0 | (c >> 18);
93
+ view[it.offset+1] = 0x80 | (c >> 12 & 0x3f);
94
+ view[it.offset+2] = 0x80 | (c >> 6 & 0x3f);
95
+ view[it.offset+3] = 0x80 | (c & 0x3f);
96
+ it.offset += 4;
86
97
  }
87
98
  }
88
99
  }
89
100
 
90
- export function int8(bytes: BufferLike, value: number, it: Iterator) {
101
+ function int8(bytes: BufferLike, value: number, it: Iterator) {
91
102
  bytes[it.offset++] = value & 255;
92
103
  };
93
104
 
94
- export function uint8(bytes: BufferLike, value: number, it: Iterator) {
105
+ function uint8(bytes: BufferLike, value: number, it: Iterator) {
95
106
  bytes[it.offset++] = value & 255;
96
107
  };
97
108
 
98
- export function int16(bytes: BufferLike, value: number, it: Iterator) {
109
+ function int16(bytes: BufferLike, value: number, it: Iterator) {
99
110
  bytes[it.offset++] = value & 255;
100
111
  bytes[it.offset++] = (value >> 8) & 255;
101
112
  };
102
113
 
103
- export function uint16(bytes: BufferLike, value: number, it: Iterator) {
114
+ function uint16(bytes: BufferLike, value: number, it: Iterator) {
104
115
  bytes[it.offset++] = value & 255;
105
116
  bytes[it.offset++] = (value >> 8) & 255;
106
117
  };
107
118
 
108
- export function int32(bytes: BufferLike, value: number, it: Iterator) {
119
+ function int32(bytes: BufferLike, value: number, it: Iterator) {
109
120
  bytes[it.offset++] = value & 255;
110
121
  bytes[it.offset++] = (value >> 8) & 255;
111
122
  bytes[it.offset++] = (value >> 16) & 255;
112
123
  bytes[it.offset++] = (value >> 24) & 255;
113
124
  };
114
125
 
115
- export function uint32(bytes: BufferLike, value: number, it: Iterator) {
126
+ function uint32(bytes: BufferLike, value: number, it: Iterator) {
116
127
  const b4 = value >> 24;
117
128
  const b3 = value >> 16;
118
129
  const b2 = value >> 8;
@@ -123,50 +134,48 @@ export function uint32(bytes: BufferLike, value: number, it: Iterator) {
123
134
  bytes[it.offset++] = b4 & 255;
124
135
  };
125
136
 
126
- export function int64(bytes: BufferLike, value: number, it: Iterator) {
137
+ function int64(bytes: BufferLike, value: number, it: Iterator) {
127
138
  const high = Math.floor(value / Math.pow(2, 32));
128
139
  const low = value >>> 0;
129
140
  uint32(bytes, low, it);
130
141
  uint32(bytes, high, it);
131
142
  };
132
143
 
133
- export function uint64(bytes: BufferLike, value: number, it: Iterator) {
144
+ function uint64(bytes: BufferLike, value: number, it: Iterator) {
134
145
  const high = (value / Math.pow(2, 32)) >> 0;
135
146
  const low = value >>> 0;
136
147
  uint32(bytes, low, it);
137
148
  uint32(bytes, high, it);
138
149
  };
139
150
 
140
- export function float32(bytes: BufferLike, value: number, it: Iterator) {
141
- writeFloat32(bytes, value, it);
151
+ function bigint64(bytes: BufferLike, value: bigint, it: Iterator) {
152
+ _int64[0] = BigInt.asIntN(64, value);
153
+ int32(bytes, _int32[0], it);
154
+ int32(bytes, _int32[1], it);
142
155
  }
143
156
 
144
- export function float64(bytes: BufferLike, value: number, it: Iterator) {
145
- writeFloat64(bytes, value, it);
157
+ function biguint64(bytes: BufferLike, value: bigint, it: Iterator) {
158
+ _int64[0] = BigInt.asIntN(64, value);
159
+ int32(bytes, _int32[0], it);
160
+ int32(bytes, _int32[1], it);
146
161
  }
147
162
 
148
- // force little endian to facilitate decoding on multiple implementations
149
- const _isLittleEndian = true; // new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;
150
- const _int32 = new Int32Array(2);
151
- const _float32 = new Float32Array(_int32.buffer);
152
- const _float64 = new Float64Array(_int32.buffer);
153
-
154
- export function writeFloat32(bytes: BufferLike, value: number, it: Iterator) {
163
+ function float32(bytes: BufferLike, value: number, it: Iterator) {
155
164
  _float32[0] = value;
156
165
  int32(bytes, _int32[0], it);
157
- };
166
+ }
158
167
 
159
- export function writeFloat64(bytes: BufferLike, value: number, it: Iterator) {
168
+ function float64(bytes: BufferLike, value: number, it: Iterator) {
160
169
  _float64[0] = value;
161
170
  int32(bytes, _int32[_isLittleEndian ? 0 : 1], it);
162
171
  int32(bytes, _int32[_isLittleEndian ? 1 : 0], it);
163
- };
172
+ }
164
173
 
165
- export function boolean(bytes: BufferLike, value: number, it: Iterator) {
174
+ function boolean(bytes: BufferLike, value: number, it: Iterator) {
166
175
  bytes[it.offset++] = value ? 1 : 0; // uint8
167
176
  };
168
177
 
169
- export function string(bytes: BufferLike, value: string, it: Iterator) {
178
+ function string(bytes: BufferLike, value: string, it: Iterator) {
170
179
  // encode `null` strings as empty.
171
180
  if (!value) { value = ""; }
172
181
 
@@ -204,7 +213,7 @@ export function string(bytes: BufferLike, value: string, it: Iterator) {
204
213
  return size + length;
205
214
  }
206
215
 
207
- export function number(bytes: BufferLike, value: number, it: Iterator) {
216
+ function number(bytes: BufferLike, value: number, it: Iterator) {
208
217
  if (isNaN(value)) {
209
218
  return number(bytes, 0, it);
210
219
 
@@ -212,17 +221,19 @@ export function number(bytes: BufferLike, value: number, it: Iterator) {
212
221
  return number(bytes, (value > 0) ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER, it);
213
222
 
214
223
  } else if (value !== (value|0)) {
224
+ if (Math.abs(value) <= 3.4028235e+38) { // range check
225
+ _float32[0] = value;
226
+ if (Math.abs(Math.abs(_float32[0]) - Math.abs(value)) < 1e-4) { // precision check; adjust 1e-n (n = precision) to in-/decrease acceptable precision loss
227
+ // now we know value is in range for f32 and has acceptable precision for f32
228
+ bytes[it.offset++] = 0xca;
229
+ float32(bytes, value, it);
230
+ return 5;
231
+ }
232
+ }
233
+
215
234
  bytes[it.offset++] = 0xcb;
216
- writeFloat64(bytes, value, it);
235
+ float64(bytes, value, it);
217
236
  return 9;
218
-
219
- // TODO: encode float 32?
220
- // is it possible to differentiate between float32 / float64 here?
221
-
222
- // // float 32
223
- // bytes.push(0xca);
224
- // writeFloat32(bytes, value);
225
- // return 5;
226
237
  }
227
238
 
228
239
  if (value >= 0) {
@@ -293,3 +304,23 @@ export function number(bytes: BufferLike, value: number, it: Iterator) {
293
304
  return 9;
294
305
  }
295
306
  }
307
+
308
+ export const encode = {
309
+ int8,
310
+ uint8,
311
+ int16,
312
+ uint16,
313
+ int32,
314
+ uint32,
315
+ int64,
316
+ uint64,
317
+ bigint64,
318
+ biguint64,
319
+ float32,
320
+ float64,
321
+ boolean,
322
+ string,
323
+ number,
324
+ utf8Write,
325
+ utf8Length,
326
+ }
@@ -8,8 +8,8 @@ export enum OPERATION {
8
8
  ADD = 128, // (10000000) add new structure/primitive
9
9
  REPLACE = 0, // (00000001) replace structure/primitive
10
10
  DELETE = 64, // (01000000) delete field
11
- DELETE_AND_MOVE = 96, // () add new structure/primitive
12
- MOVE_AND_ADD = 160, // () add new structure/primitive
11
+ DELETE_AND_MOVE = 96, // () ArraySchema only
12
+ MOVE_AND_ADD = 160, // () ArraySchema only
13
13
  DELETE_AND_ADD = 192, // (11000000) DELETE field, followed by an ADD
14
14
 
15
15
  /**
@@ -20,10 +20,8 @@ export enum OPERATION {
20
20
  /**
21
21
  * ArraySchema operations
22
22
  */
23
- PUSH = 11,
24
- UNSHIFT = 12,
25
23
  REVERSE = 15,
26
24
  MOVE = 32,
27
25
  DELETE_BY_REFID = 33, // This operation is only used at ENCODING time. During DECODING, DELETE_BY_REFID is converted to DELETE
28
-
26
+ ADD_BY_REFID = 129,
29
27
  }
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 };
@@ -16,8 +14,8 @@ export { CollectionSchema };
16
14
  import { SetSchema } from "./types/custom/SetSchema";
17
15
  export { SetSchema };
18
16
 
19
- import { registerType } from "./types/registry";
20
- export { registerType };
17
+ import { registerType, defineCustomTypes } from "./types/registry";
18
+ export { registerType, defineCustomTypes };
21
19
 
22
20
  registerType("map", { constructor: MapSchema });
23
21
  registerType("array", { constructor: ArraySchema });
@@ -28,10 +26,9 @@ registerType("collection", { constructor: CollectionSchema, });
28
26
  export { dumpChanges } from "./utils";
29
27
 
30
28
  // Encoder / Decoder
31
- export type { Iterator } from "./encoding/decode";
32
- import * as encode from "./encoding/encode";
33
- import * as decode from "./encoding/decode";
34
- export { encode, decode };
29
+ export { $track, $encoder, $decoder, $filter, $getByIndex, $deleteByIndex, $changes, $childType } from "./types/symbols";
30
+ export { encode } from "./encoding/encode";
31
+ export { decode, type Iterator } from "./encoding/decode";
35
32
 
36
33
  // Reflection
37
34
  export {
@@ -40,22 +37,17 @@ export {
40
37
  ReflectionField,
41
38
  } from "./Reflection";
42
39
 
40
+ // Annotations, Metadata and TypeContext
43
41
  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";
42
+ export { type, deprecated, defineTypes, view, schema, type SchemaWithExtends, } from "./annotations";
43
+ export { TypeContext } from "./types/TypeContext";
55
44
 
56
45
  // Annotation types
57
46
  export type { DefinitionType, PrimitiveType, Definition, } from "./annotations";
58
47
 
48
+ export { getDecoderStateCallbacks, CallbackProxy, GetCallbackProxy } from "./decoder/strategy/StateCallbacks";
49
+ export { getRawChangesCallback } from "./decoder/strategy/RawChanges";
50
+
59
51
  export { Encoder } from "./encoder/Encoder";
60
52
  export { encodeSchemaOperation, encodeArray as encodeKeyValueOperation } from "./encoder/EncodeOperation";
61
53
  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,175 @@
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
+ //
33
+ // TODO:
34
+ // cache "discoverTypes" results for each rootClass
35
+ // to avoid re-discovering types for each new context/room
36
+ //
37
+ this.discoverTypes(rootClass);
38
+ }
39
+ }
40
+
41
+ has(schema: typeof Schema) {
42
+ return this.schemas.has(schema);
43
+ }
44
+
45
+ get(typeid: number) {
46
+ return this.types[typeid];
47
+ }
48
+
49
+ add(schema: typeof Schema, typeid = this.schemas.size) {
50
+ // skip if already registered
51
+ if (this.schemas.has(schema)) {
52
+ return false;
53
+ }
54
+
55
+ this.types[typeid] = schema;
56
+
57
+ //
58
+ // Workaround to allow using an empty Schema (with no `@type()` fields)
59
+ //
60
+ if (schema[Symbol.metadata] === undefined) {
61
+ Metadata.initialize(schema);
62
+ }
63
+
64
+ this.schemas.set(schema, typeid);
65
+ return true;
66
+ }
67
+
68
+ getTypeId(klass: typeof Schema) {
69
+ return this.schemas.get(klass);
70
+ }
71
+
72
+ private discoverTypes(klass: typeof Schema, parentType?: typeof Schema, parentIndex?: number, parentHasViewTag?: boolean) {
73
+ if (parentHasViewTag) {
74
+ this.registerFilteredByParent(klass, parentType, parentIndex);
75
+ }
76
+
77
+ // skip if already registered
78
+ if (!this.add(klass)) { return; }
79
+
80
+ // add classes inherited from this base class
81
+ TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
82
+ this.discoverTypes(child, parentType, parentIndex, parentHasViewTag);
83
+ });
84
+
85
+ // add parent classes
86
+ let parent: any = klass;
87
+ while (
88
+ (parent = Object.getPrototypeOf(parent)) &&
89
+ parent !== Schema && // stop at root (Schema)
90
+ parent !== Function.prototype // stop at root (non-Schema)
91
+ ) {
92
+ this.discoverTypes(parent);
93
+ }
94
+
95
+ const metadata: Metadata = (klass[Symbol.metadata] ??= {});
96
+
97
+ // if any schema/field has filters, mark "context" as having filters.
98
+ if (metadata[$viewFieldIndexes]) {
99
+ this.hasFilters = true;
100
+ }
101
+
102
+ for (const fieldIndex in metadata) {
103
+ const index = fieldIndex as any as number;
104
+
105
+ const fieldType = metadata[index].type;
106
+ const fieldHasViewTag = (metadata[index].tag !== undefined);
107
+
108
+ if (typeof (fieldType) === "string") {
109
+ continue;
110
+ }
111
+
112
+ if (Array.isArray(fieldType)) {
113
+ const type = fieldType[0];
114
+
115
+ // skip primitive types
116
+ if (type === "string") {
117
+ continue;
118
+ }
119
+
120
+ this.discoverTypes(type as typeof Schema, klass, index, parentHasViewTag || fieldHasViewTag);
121
+
122
+ } else if (typeof (fieldType) === "function") {
123
+ this.discoverTypes(fieldType as typeof Schema, klass, index, parentHasViewTag || fieldHasViewTag);
124
+
125
+ } else {
126
+ const type = Object.values(fieldType)[0];
127
+
128
+ // skip primitive types
129
+ if (typeof (type) === "string") {
130
+ continue;
131
+ }
132
+
133
+ this.discoverTypes(type as typeof Schema, klass, index, parentHasViewTag || fieldHasViewTag);
134
+ }
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Keep track of which classes have filters applied.
140
+ * Format: `${typeid}-${parentTypeid}-${parentIndex}`
141
+ */
142
+ private registerFilteredByParent(schema: typeof Schema, parentType?: typeof Schema, parentIndex?: number) {
143
+ const typeid = this.schemas.get(schema) ?? this.schemas.size;
144
+
145
+ let key = `${typeid}`;
146
+ if (parentType) { key += `-${this.schemas.get(parentType)}`; }
147
+
148
+ key += `-${parentIndex}`;
149
+ this.parentFiltered[key] = true;
150
+ }
151
+
152
+ debug() {
153
+ let parentFiltered = "";
154
+
155
+ for (const key in this.parentFiltered) {
156
+ const keys: number[] = key.split("-").map(Number);
157
+ const fieldIndex = keys.pop();
158
+
159
+ parentFiltered += `\n\t\t`;
160
+ parentFiltered += `${key}: ${keys.reverse().map((id, i) => {
161
+ const klass = this.types[id];
162
+ const metadata: Metadata = klass[Symbol.metadata];
163
+ let txt = klass.name;
164
+ if (i === 0) { txt += `[${metadata[fieldIndex].name}]`; }
165
+ return `${txt}`;
166
+ }).join(" -> ")}`;
167
+ }
168
+
169
+ return `TypeContext ->\n` +
170
+ `\tSchema types: ${this.schemas.size}\n` +
171
+ `\thasFilters: ${this.hasFilters}\n` +
172
+ `\tparentFiltered:${parentFiltered}`;
173
+ }
174
+
175
+ }