@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.
- package/build/cjs/index.js +34 -33
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +112 -83
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +36 -35
- package/lib/Reflection.js +11 -11
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.js +14 -13
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.js +17 -13
- package/lib/annotations.js.map +1 -1
- package/lib/changes/ChangeTree.js +2 -2
- package/lib/changes/ChangeTree.js.map +1 -1
- package/lib/codegen/api.js +1 -1
- package/lib/codegen/api.js.map +1 -1
- package/lib/codegen/argv.d.ts +1 -1
- package/lib/codegen/cli.js +5 -5
- package/lib/codegen/cli.js.map +1 -1
- package/lib/codegen/languages/cpp.js +38 -38
- package/lib/codegen/languages/cpp.js.map +1 -1
- package/lib/codegen/languages/csharp.js +25 -21
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +13 -13
- package/lib/codegen/languages/haxe.js.map +1 -1
- package/lib/codegen/languages/java.js +11 -11
- package/lib/codegen/languages/java.js.map +1 -1
- package/lib/codegen/languages/js.js +16 -16
- package/lib/codegen/languages/js.js.map +1 -1
- package/lib/codegen/languages/lua.js +12 -12
- package/lib/codegen/languages/lua.js.map +1 -1
- package/lib/codegen/languages/ts.js +37 -33
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +3 -3
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +2 -2
- package/lib/codegen/types.js.map +1 -1
- package/lib/events/EventEmitter.js +10 -6
- package/lib/events/EventEmitter.js.map +1 -1
- package/lib/index.js +4 -4
- package/lib/index.js.map +1 -1
- package/lib/types/ArraySchema.js +12 -8
- package/lib/types/ArraySchema.js.map +1 -1
- package/lib/types/MapSchema.js +1 -1
- package/lib/types/MapSchema.js.map +1 -1
- package/package.json +2 -1
- package/src/Reflection.ts +159 -0
- package/src/Schema.ts +1084 -0
- package/src/annotations.ts +357 -0
- package/src/changes/ChangeTree.ts +373 -0
- package/src/codegen/api.ts +46 -0
- package/src/codegen/argv.ts +40 -0
- package/src/codegen/cli.ts +65 -0
- package/src/codegen/languages/cpp.ts +297 -0
- package/src/codegen/languages/csharp.ts +119 -0
- package/src/codegen/languages/haxe.ts +110 -0
- package/src/codegen/languages/java.ts +115 -0
- package/src/codegen/languages/js.ts +115 -0
- package/src/codegen/languages/lua.ts +125 -0
- package/src/codegen/languages/ts.ts +129 -0
- package/src/codegen/parser.ts +251 -0
- package/src/codegen/types.ts +164 -0
- package/src/encoding/decode.ts +278 -0
- package/src/encoding/encode.ts +283 -0
- package/src/events/EventEmitter.ts +32 -0
- package/src/filters/index.ts +23 -0
- package/src/index.ts +59 -0
- package/src/spec.ts +49 -0
- package/src/types/ArraySchema.ts +608 -0
- package/src/types/CollectionSchema.ts +188 -0
- package/src/types/HelperTypes.ts +34 -0
- package/src/types/MapSchema.ts +255 -0
- package/src/types/SetSchema.ts +197 -0
- package/src/types/index.ts +19 -0
- 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
|
+
}
|