@colyseus/schema 1.0.35 → 1.0.36

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 (74) hide show
  1. package/build/cjs/index.js +34 -33
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/index.mjs +112 -83
  4. package/build/esm/index.mjs.map +1 -1
  5. package/build/umd/index.js +36 -35
  6. package/lib/Reflection.js +11 -11
  7. package/lib/Reflection.js.map +1 -1
  8. package/lib/Schema.js +14 -13
  9. package/lib/Schema.js.map +1 -1
  10. package/lib/annotations.js +17 -13
  11. package/lib/annotations.js.map +1 -1
  12. package/lib/changes/ChangeTree.js +2 -2
  13. package/lib/changes/ChangeTree.js.map +1 -1
  14. package/lib/codegen/api.js +1 -1
  15. package/lib/codegen/api.js.map +1 -1
  16. package/lib/codegen/argv.d.ts +1 -1
  17. package/lib/codegen/cli.js +5 -5
  18. package/lib/codegen/cli.js.map +1 -1
  19. package/lib/codegen/languages/cpp.js +38 -38
  20. package/lib/codegen/languages/cpp.js.map +1 -1
  21. package/lib/codegen/languages/csharp.js +25 -21
  22. package/lib/codegen/languages/csharp.js.map +1 -1
  23. package/lib/codegen/languages/haxe.js +13 -13
  24. package/lib/codegen/languages/haxe.js.map +1 -1
  25. package/lib/codegen/languages/java.js +11 -11
  26. package/lib/codegen/languages/java.js.map +1 -1
  27. package/lib/codegen/languages/js.js +16 -16
  28. package/lib/codegen/languages/js.js.map +1 -1
  29. package/lib/codegen/languages/lua.js +12 -12
  30. package/lib/codegen/languages/lua.js.map +1 -1
  31. package/lib/codegen/languages/ts.js +37 -33
  32. package/lib/codegen/languages/ts.js.map +1 -1
  33. package/lib/codegen/parser.js +3 -3
  34. package/lib/codegen/parser.js.map +1 -1
  35. package/lib/codegen/types.js +2 -2
  36. package/lib/codegen/types.js.map +1 -1
  37. package/lib/events/EventEmitter.js +10 -6
  38. package/lib/events/EventEmitter.js.map +1 -1
  39. package/lib/index.js +4 -4
  40. package/lib/index.js.map +1 -1
  41. package/lib/types/ArraySchema.js +12 -8
  42. package/lib/types/ArraySchema.js.map +1 -1
  43. package/lib/types/MapSchema.js +1 -1
  44. package/lib/types/MapSchema.js.map +1 -1
  45. package/package.json +2 -1
  46. package/src/Reflection.ts +159 -0
  47. package/src/Schema.ts +1084 -0
  48. package/src/annotations.ts +357 -0
  49. package/src/changes/ChangeTree.ts +373 -0
  50. package/src/codegen/api.ts +46 -0
  51. package/src/codegen/argv.ts +40 -0
  52. package/src/codegen/cli.ts +65 -0
  53. package/src/codegen/languages/cpp.ts +297 -0
  54. package/src/codegen/languages/csharp.ts +119 -0
  55. package/src/codegen/languages/haxe.ts +110 -0
  56. package/src/codegen/languages/java.ts +115 -0
  57. package/src/codegen/languages/js.ts +115 -0
  58. package/src/codegen/languages/lua.ts +125 -0
  59. package/src/codegen/languages/ts.ts +129 -0
  60. package/src/codegen/parser.ts +251 -0
  61. package/src/codegen/types.ts +164 -0
  62. package/src/encoding/decode.ts +278 -0
  63. package/src/encoding/encode.ts +283 -0
  64. package/src/events/EventEmitter.ts +32 -0
  65. package/src/filters/index.ts +23 -0
  66. package/src/index.ts +59 -0
  67. package/src/spec.ts +49 -0
  68. package/src/types/ArraySchema.ts +608 -0
  69. package/src/types/CollectionSchema.ts +188 -0
  70. package/src/types/HelperTypes.ts +34 -0
  71. package/src/types/MapSchema.ts +255 -0
  72. package/src/types/SetSchema.ts +197 -0
  73. package/src/types/index.ts +19 -0
  74. package/src/utils.ts +28 -0
@@ -0,0 +1,357 @@
1
+ import { ChangeTree } from './changes/ChangeTree';
2
+ import { Schema } from './Schema';
3
+ import { ArraySchema, getArrayProxy } from './types/ArraySchema';
4
+ import { MapSchema, getMapProxy } from './types/MapSchema';
5
+ import { getType } from './types';
6
+
7
+ /**
8
+ * Data types
9
+ */
10
+ export type PrimitiveType =
11
+ "string" |
12
+ "number" |
13
+ "boolean" |
14
+ "int8" |
15
+ "uint8" |
16
+ "int16" |
17
+ "uint16" |
18
+ "int32" |
19
+ "uint32" |
20
+ "int64" |
21
+ "uint64" |
22
+ "float32" |
23
+ "float64" |
24
+ typeof Schema;
25
+
26
+ export type DefinitionType = PrimitiveType
27
+ | PrimitiveType[]
28
+ | { array: PrimitiveType }
29
+ | { map: PrimitiveType }
30
+ | { collection: PrimitiveType }
31
+ | { set: PrimitiveType };
32
+
33
+ export type Definition = { [field: string]: DefinitionType };
34
+ export type FilterCallback<
35
+ T extends Schema = any,
36
+ V = any,
37
+ R extends Schema = any
38
+ > = (
39
+ ((this: T, client: ClientWithSessionId, value: V) => boolean) |
40
+ ((this: T, client: ClientWithSessionId, value: V, root: R) => boolean)
41
+ );
42
+
43
+ export type FilterChildrenCallback<
44
+ T extends Schema = any,
45
+ K = any,
46
+ V = any,
47
+ R extends Schema = any
48
+ > = (
49
+ ((this: T, client: ClientWithSessionId, key: K, value: V) => boolean) |
50
+ ((this: T, client: ClientWithSessionId, key: K, value: V, root: R) => boolean)
51
+ )
52
+
53
+ export class SchemaDefinition {
54
+ schema: Definition;
55
+
56
+ //
57
+ // TODO: use a "field" structure combining all these properties per-field.
58
+ //
59
+
60
+ indexes: { [field: string]: number } = {};
61
+ fieldsByIndex: { [index: number]: string } = {};
62
+
63
+ filters: { [field: string]: FilterCallback };
64
+ indexesWithFilters: number[];
65
+ childFilters: { [field: string]: FilterChildrenCallback }; // childFilters are used on Map, Array, Set items.
66
+
67
+ deprecated: { [field: string]: boolean } = {};
68
+ descriptors: PropertyDescriptorMap & ThisType<any> = {};
69
+
70
+ static create(parent?: SchemaDefinition) {
71
+ const definition = new SchemaDefinition();
72
+
73
+ // support inheritance
74
+ definition.schema = Object.assign({}, parent && parent.schema || {});
75
+ definition.indexes = Object.assign({}, parent && parent.indexes || {});
76
+ definition.fieldsByIndex = Object.assign({}, parent && parent.fieldsByIndex || {});
77
+ definition.descriptors = Object.assign({}, parent && parent.descriptors || {});
78
+ definition.deprecated = Object.assign({}, parent && parent.deprecated || {});
79
+
80
+ return definition;
81
+ }
82
+
83
+ addField(field: string, type: DefinitionType) {
84
+ const index = this.getNextFieldIndex();
85
+ this.fieldsByIndex[index] = field;
86
+ this.indexes[field] = index;
87
+ this.schema[field] = (Array.isArray(type))
88
+ ? { array: type[0] }
89
+ : type;
90
+ }
91
+
92
+ addFilter(field: string, cb: FilterCallback) {
93
+ if (!this.filters) {
94
+ this.filters = {};
95
+ this.indexesWithFilters = [];
96
+ }
97
+ this.filters[this.indexes[field]] = cb;
98
+ this.indexesWithFilters.push(this.indexes[field]);
99
+ return true;
100
+ }
101
+
102
+ addChildrenFilter(field: string, cb: FilterChildrenCallback) {
103
+ const index = this.indexes[field];
104
+ const type = this.schema[field];
105
+
106
+ if (getType(Object.keys(type)[0])) {
107
+ if (!this.childFilters) { this.childFilters = {}; }
108
+
109
+ this.childFilters[index] = cb;
110
+ return true;
111
+
112
+ } else {
113
+ console.warn(`@filterChildren: field '${field}' can't have children. Ignoring filter.`);
114
+ }
115
+ }
116
+
117
+ getChildrenFilter(field: string) {
118
+ return this.childFilters && this.childFilters[this.indexes[field]];
119
+ }
120
+
121
+ getNextFieldIndex() {
122
+ return Object.keys(this.schema || {}).length;
123
+ }
124
+ }
125
+
126
+ export function hasFilter(klass: typeof Schema) {
127
+ return klass._context && klass._context.useFilters;
128
+ }
129
+
130
+ // Colyseus integration
131
+ export type ClientWithSessionId = { sessionId: string } & any;
132
+
133
+ export class Context {
134
+ types: {[id: number]: typeof Schema} = {};
135
+ schemas = new Map<typeof Schema, number>();
136
+ useFilters = false;
137
+
138
+ has(schema: typeof Schema) {
139
+ return this.schemas.has(schema);
140
+ }
141
+
142
+ get(typeid: number) {
143
+ return this.types[typeid];
144
+ }
145
+
146
+ add(schema: typeof Schema, typeid: number = this.schemas.size) {
147
+ // FIXME: move this to somewhere else?
148
+ // support inheritance
149
+ schema._definition = SchemaDefinition.create(schema._definition);
150
+
151
+ schema._typeid = typeid;
152
+ this.types[typeid] = schema;
153
+ this.schemas.set(schema, typeid);
154
+ }
155
+
156
+ static create(context: Context = new Context) {
157
+ return function (definition: DefinitionType) {
158
+ return type(definition, context);
159
+ }
160
+ }
161
+ }
162
+
163
+ export const globalContext = new Context();
164
+
165
+ /**
166
+ * `@type()` decorator for proxies
167
+ */
168
+ export function type (type: DefinitionType, context: Context = globalContext): PropertyDecorator {
169
+ return function (target: typeof Schema, field: string) {
170
+ if (!type) {
171
+ throw new Error("Type not found. Ensure your `@type` annotations are correct and that you don't have any circular dependencies.");
172
+ }
173
+
174
+ const constructor = target.constructor as typeof Schema;
175
+ constructor._context = context;
176
+
177
+ /*
178
+ * static schema
179
+ */
180
+ if (!context.has(constructor)) {
181
+ context.add(constructor);
182
+ }
183
+
184
+ const definition = constructor._definition;
185
+ definition.addField(field, type);
186
+
187
+ /**
188
+ * skip if descriptor already exists for this field (`@deprecated()`)
189
+ */
190
+ if (definition.descriptors[field]) {
191
+ if (definition.deprecated[field]) {
192
+ // do not create accessors for deprecated properties.
193
+ return;
194
+
195
+ } else {
196
+ // trying to define same property multiple times across inheritance.
197
+ // https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
198
+ try {
199
+ throw new Error(`@colyseus/schema: Duplicate '${field}' definition on '${constructor.name}'.\nCheck @type() annotation`);
200
+
201
+ } catch (e) {
202
+ const definitionAtLine = e.stack.split("\n")[4].trim();
203
+ throw new Error(`${e.message} ${definitionAtLine}`);
204
+ }
205
+ }
206
+ }
207
+
208
+ const isArray = ArraySchema.is(type);
209
+ const isMap = !isArray && MapSchema.is(type);
210
+
211
+ // TODO: refactor me.
212
+ // Allow abstract intermediary classes with no fields to be serialized
213
+ // (See "should support an inheritance with a Schema type without fields" test)
214
+ if (typeof (type) !== "string" && !Schema.is(type)) {
215
+ const childType = Object.values(type)[0];
216
+ if (typeof (childType) !== "string" && !context.has(childType)) {
217
+ context.add(childType);
218
+ }
219
+ }
220
+
221
+ const fieldCached = `_${field}`;
222
+
223
+ definition.descriptors[fieldCached] = {
224
+ enumerable: false,
225
+ configurable: false,
226
+ writable: true,
227
+ };
228
+
229
+ definition.descriptors[field] = {
230
+ get: function () {
231
+ return this[fieldCached];
232
+ },
233
+
234
+ set: function (this: Schema, value: any) {
235
+ /**
236
+ * Create Proxy for array or map items
237
+ */
238
+
239
+ // skip if value is the same as cached.
240
+ if (value === this[fieldCached]) {
241
+ return;
242
+ }
243
+
244
+ if (
245
+ value !== undefined &&
246
+ value !== null
247
+ ) {
248
+ // automaticallty transform Array into ArraySchema
249
+ if (isArray && !(value instanceof ArraySchema)) {
250
+ value = new ArraySchema(...value);
251
+ }
252
+
253
+ // automaticallty transform Map into MapSchema
254
+ if (isMap && !(value instanceof MapSchema)) {
255
+ value = new MapSchema(value);
256
+ }
257
+
258
+ // try to turn provided structure into a Proxy
259
+ if (value['$proxy'] === undefined) {
260
+ if (isMap) {
261
+ value = getMapProxy(value);
262
+
263
+ } else if (isArray) {
264
+ value = getArrayProxy(value);
265
+ }
266
+ }
267
+
268
+ // flag the change for encoding.
269
+ this.$changes.change(field);
270
+
271
+ //
272
+ // call setParent() recursively for this and its child
273
+ // structures.
274
+ //
275
+ if (value['$changes']) {
276
+ (value['$changes'] as ChangeTree).setParent(
277
+ this,
278
+ this.$changes.root,
279
+ this._definition.indexes[field],
280
+ );
281
+ }
282
+
283
+ } else {
284
+ //
285
+ // Setting a field to `null` or `undefined` will delete it.
286
+ //
287
+ this.$changes.delete(field);
288
+ }
289
+
290
+ this[fieldCached] = value;
291
+ },
292
+
293
+ enumerable: true,
294
+ configurable: true
295
+ };
296
+ }
297
+ }
298
+
299
+ /**
300
+ * `@filter()` decorator for defining data filters per client
301
+ */
302
+
303
+ export function filter<T extends Schema, V, R extends Schema>(cb: FilterCallback<T, V, R>): PropertyDecorator {
304
+ return function (target: any, field: string) {
305
+ const constructor = target.constructor as typeof Schema;
306
+ const definition = constructor._definition;
307
+
308
+ if (definition.addFilter(field, cb)) {
309
+ constructor._context.useFilters = true;
310
+ }
311
+ }
312
+ }
313
+
314
+ export function filterChildren<T extends Schema, K, V, R extends Schema>(cb: FilterChildrenCallback<T, K, V, R>): PropertyDecorator {
315
+ return function (target: any, field: string) {
316
+ const constructor = target.constructor as typeof Schema;
317
+ const definition = constructor._definition;
318
+ if (definition.addChildrenFilter(field, cb)) {
319
+ constructor._context.useFilters = true;
320
+ }
321
+ }
322
+ }
323
+
324
+
325
+ /**
326
+ * `@deprecated()` flag a field as deprecated.
327
+ * The previous `@type()` annotation should remain along with this one.
328
+ */
329
+
330
+ export function deprecated(throws: boolean = true, context: Context = globalContext): PropertyDecorator {
331
+ return function (target: typeof Schema, field: string) {
332
+ const constructor = target.constructor as typeof Schema;
333
+ const definition = constructor._definition;
334
+
335
+ definition.deprecated[field] = true;
336
+
337
+ if (throws) {
338
+ definition.descriptors[field] = {
339
+ get: function () { throw new Error(`${field} is deprecated.`); },
340
+ set: function (this: Schema, value: any) { /* throw new Error(`${field} is deprecated.`); */ },
341
+ enumerable: false,
342
+ configurable: true
343
+ };
344
+ }
345
+ }
346
+ }
347
+
348
+ export function defineTypes(
349
+ target: typeof Schema,
350
+ fields: { [property: string]: DefinitionType },
351
+ context: Context = target._context || globalContext
352
+ ) {
353
+ for (let field in fields) {
354
+ type(fields[field], context)(target.prototype, field);
355
+ }
356
+ return target;
357
+ }