@colyseus/schema
Advanced tools
Comparing version 0.4.24 to 0.4.25-alpha.0
@@ -1,4 +0,2 @@ | ||
import * as decode from "./encoding/decode"; | ||
import { ChangeTree } from './ChangeTree'; | ||
import { ArraySchema } from './types/ArraySchema'; | ||
import { Schema } from './Schema'; | ||
/** | ||
@@ -18,62 +16,16 @@ * Data types | ||
} & any; | ||
export interface DataChange<T = any> { | ||
field: string; | ||
value: T; | ||
previousValue: T; | ||
} | ||
/** | ||
* Schema encoder / decoder | ||
*/ | ||
export declare abstract class Schema { | ||
static _schema: Definition; | ||
static _indexes: { | ||
[field: string]: number; | ||
export declare class Context { | ||
types: { | ||
[id: number]: typeof Schema; | ||
}; | ||
static _filters: { | ||
[field: string]: FilterCallback; | ||
}; | ||
static _descriptors: PropertyDescriptorMap & ThisType<any>; | ||
protected $changes: ChangeTree; | ||
onChange?(changes: DataChange[]): any; | ||
onRemove?(): any; | ||
constructor(...args: any[]); | ||
readonly _schema: Definition; | ||
readonly _descriptors: PropertyDescriptorMap & ThisType<any>; | ||
readonly _indexes: { | ||
[field: string]: number; | ||
}; | ||
readonly _filters: { | ||
[field: string]: FilterCallback; | ||
}; | ||
readonly $changed: boolean; | ||
decode(bytes: any, it?: decode.Iterator): this; | ||
encode(root?: Schema, encodeAll?: boolean, client?: Client): any[]; | ||
encodeFiltered(client: Client): any[]; | ||
encodeAll(): any[]; | ||
encodeAllFiltered(client: Client): any[]; | ||
clone(): any; | ||
triggerAll(): void; | ||
toJSON(): {}; | ||
schemas: Map<typeof Schema, number>; | ||
has(schema: typeof Schema): boolean; | ||
get(typeid: number): typeof Schema; | ||
add(schema: typeof Schema): void; | ||
} | ||
export declare const globalContext: Context; | ||
/** | ||
* Reflection | ||
*/ | ||
export declare class ReflectionField extends Schema { | ||
name: string; | ||
type: string; | ||
referencedType: number; | ||
} | ||
export declare class ReflectionType extends Schema { | ||
id: number; | ||
fields: ArraySchema<ReflectionField>; | ||
} | ||
export declare class Reflection extends Schema { | ||
types: ArraySchema<ReflectionType>; | ||
static encode(instance: Schema): any[]; | ||
static decode(bytes: number[]): Schema; | ||
} | ||
/** | ||
* `@type()` decorator for proxies | ||
*/ | ||
export declare function type(type: DefinitionType): PropertyDecorator; | ||
export declare function type(type: DefinitionType, context?: Context): PropertyDecorator; | ||
/** | ||
@@ -80,0 +32,0 @@ * `@filter()` decorator for defining data filters per client |
"use strict"; | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; | ||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); | ||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; | ||
return c > 3 && r && Object.defineProperty(target, key, r), r; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var spec_1 = require("./spec"); | ||
var encode = require("./encoding/encode"); | ||
var decode = require("./encoding/decode"); | ||
var ChangeTree_1 = require("./ChangeTree"); | ||
var ArraySchema_1 = require("./types/ArraySchema"); | ||
var MapSchema_1 = require("./types/MapSchema"); | ||
var definedSchemas = new Map(); | ||
var EncodeSchemaError = /** @class */ (function (_super) { | ||
__extends(EncodeSchemaError, _super); | ||
function EncodeSchemaError() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
var Schema_1 = require("./Schema"); | ||
var Context = /** @class */ (function () { | ||
function Context() { | ||
this.types = {}; | ||
this.schemas = new Map(); | ||
} | ||
return EncodeSchemaError; | ||
}(Error)); | ||
function assertType(value, type, klass, field) { | ||
var typeofTarget; | ||
var allowNull = false; | ||
switch (type) { | ||
case "number": | ||
case "int8": | ||
case "uint8": | ||
case "int16": | ||
case "uint16": | ||
case "int32": | ||
case "uint32": | ||
case "int64": | ||
case "uint64": | ||
case "float32": | ||
case "float64": | ||
typeofTarget = "number"; | ||
break; | ||
case "string": | ||
typeofTarget = "string"; | ||
allowNull = true; | ||
break; | ||
case "boolean": | ||
// boolean is always encoded as true/false based on truthiness | ||
return; | ||
} | ||
if (typeof (value) !== typeofTarget && (!allowNull || (allowNull && value !== null))) { | ||
var foundValue = "'" + JSON.stringify(value) + "'" + (value && value.constructor && " (" + value.constructor.name + ")"); | ||
throw new EncodeSchemaError("a '" + typeofTarget + "' was expected, but " + foundValue + " was provided in " + klass.constructor.name + "#" + field); | ||
} | ||
} | ||
function assertInstanceType(value, type, klass, field) { | ||
if (value.constructor !== type) { | ||
throw new EncodeSchemaError("a '" + type.name + "' was expected, but '" + value.constructor.name + "' was provided in " + klass.constructor.name + "#" + field); | ||
} | ||
} | ||
function encodePrimitiveType(type, bytes, value) { | ||
var encodeFunc = encode[type]; | ||
if (encodeFunc) { | ||
encodeFunc(bytes, value); | ||
return true; | ||
} | ||
else { | ||
return false; | ||
} | ||
} | ||
function decodePrimitiveType(type, bytes, it) { | ||
var decodeFunc = decode[type]; | ||
if (decodeFunc) { | ||
return decodeFunc(bytes, it); | ||
} | ||
else { | ||
return null; | ||
} | ||
} | ||
/** | ||
* Schema encoder / decoder | ||
*/ | ||
var Schema = /** @class */ (function () { | ||
// allow inherited classes to have a constructor | ||
function Schema() { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
// fix enumerability of fields for end-user | ||
Object.defineProperties(this, { | ||
$changes: { value: new ChangeTree_1.ChangeTree(), enumerable: false, writable: true }, | ||
}); | ||
var descriptors = this._descriptors; | ||
if (descriptors) { | ||
Object.defineProperties(this, descriptors); | ||
} | ||
} | ||
Object.defineProperty(Schema.prototype, "_schema", { | ||
get: function () { return this.constructor._schema; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Schema.prototype, "_descriptors", { | ||
get: function () { return this.constructor._descriptors; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Schema.prototype, "_indexes", { | ||
get: function () { return this.constructor._indexes; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Schema.prototype, "_filters", { | ||
get: function () { return this.constructor._filters; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Schema.prototype, "$changed", { | ||
get: function () { return this.$changes.changed; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Schema.prototype.decode = function (bytes, it) { | ||
if (it === void 0) { it = { offset: 0 }; } | ||
var changes = []; | ||
var schema = this._schema; | ||
var indexes = this._indexes; | ||
var fieldsByIndex = {}; | ||
Object.keys(indexes).forEach(function (key) { | ||
var value = indexes[key]; | ||
fieldsByIndex[value] = key; | ||
}); | ||
var totalBytes = bytes.length; | ||
var _loop_1 = function () { | ||
var index = bytes[it.offset++]; | ||
if (index === spec_1.END_OF_STRUCTURE) { | ||
return "break"; | ||
} | ||
var field = fieldsByIndex[index]; | ||
var type_1 = schema[field]; | ||
var value = void 0; | ||
var change = void 0; // for triggering onChange | ||
var hasChange = false; | ||
if (type_1._schema) { | ||
if (decode.nilCheck(bytes, it)) { | ||
it.offset++; | ||
value = null; | ||
} | ||
else { | ||
value = this_1["_" + field] || new type_1(); | ||
value.decode(bytes, it); | ||
} | ||
hasChange = true; | ||
} | ||
else if (Array.isArray(type_1)) { | ||
type_1 = type_1[0]; | ||
change = []; | ||
var valueRef_1 = this_1["_" + field] || new ArraySchema_1.ArraySchema(); | ||
value = valueRef_1.clone(); | ||
var newLength_1 = decode.number(bytes, it); | ||
var numChanges = decode.number(bytes, it); | ||
hasChange = (numChanges > 0); | ||
// FIXME: this may not be reliable. possibly need to encode this variable during | ||
// serializagion | ||
var hasIndexChange = false; | ||
// ensure current array has the same length as encoded one | ||
if (value.length > newLength_1) { | ||
value.splice(newLength_1).forEach(function (itemRemoved, i) { | ||
if (itemRemoved.onRemove) { | ||
itemRemoved.onRemove(); | ||
} | ||
if (valueRef_1.onRemove) { | ||
valueRef_1.onRemove(itemRemoved, newLength_1 + i); | ||
} | ||
}); | ||
} | ||
for (var i = 0; i < numChanges; i++) { | ||
var newIndex = decode.number(bytes, it); | ||
var indexChangedFrom = void 0; // index change check | ||
if (decode.indexChangeCheck(bytes, it)) { | ||
decode.uint8(bytes, it); | ||
indexChangedFrom = decode.number(bytes, it); | ||
hasIndexChange = true; | ||
} | ||
var isNew = (!hasIndexChange && !value[newIndex]) || (hasIndexChange && indexChangedFrom === undefined); | ||
if (type_1.prototype instanceof Schema) { | ||
var item = void 0; | ||
if (isNew) { | ||
item = new type_1(); | ||
} | ||
else if (indexChangedFrom !== undefined) { | ||
item = valueRef_1[indexChangedFrom]; | ||
} | ||
else { | ||
item = valueRef_1[newIndex]; | ||
} | ||
if (!item) { | ||
item = new type_1(); | ||
isNew = true; | ||
} | ||
if (decode.nilCheck(bytes, it)) { | ||
it.offset++; | ||
if (valueRef_1.onRemove) { | ||
valueRef_1.onRemove(item, newIndex); | ||
} | ||
continue; | ||
} | ||
item.decode(bytes, it); | ||
value[newIndex] = item; | ||
} | ||
else { | ||
value[newIndex] = decodePrimitiveType(type_1, bytes, it); | ||
} | ||
if (isNew) { | ||
if (valueRef_1.onAdd) { | ||
valueRef_1.onAdd(value[newIndex], newIndex); | ||
} | ||
} | ||
else if (valueRef_1.onChange) { | ||
valueRef_1.onChange(value[newIndex], newIndex); | ||
} | ||
change.push(value[newIndex]); | ||
} | ||
} | ||
else if (type_1.map) { | ||
type_1 = type_1.map; | ||
var valueRef = this_1["_" + field] || new MapSchema_1.MapSchema(); | ||
value = valueRef.clone(); | ||
var length = decode.number(bytes, it); | ||
hasChange = (length > 0); | ||
// FIXME: this may not be reliable. possibly need to encode this variable during | ||
// serializagion | ||
var hasIndexChange = false; | ||
var previousKeys = Object.keys(valueRef); | ||
for (var i = 0; i < length; i++) { | ||
// `encodeAll` may indicate a higher number of indexes it actually encodes | ||
// TODO: do not encode a higher number than actual encoded entries | ||
if (bytes[it.offset] === undefined || | ||
bytes[it.offset] === spec_1.END_OF_STRUCTURE) { | ||
break; | ||
} | ||
// index change check | ||
var previousKey = void 0; | ||
if (decode.indexChangeCheck(bytes, it)) { | ||
decode.uint8(bytes, it); | ||
previousKey = previousKeys[decode.number(bytes, it)]; | ||
hasIndexChange = true; | ||
} | ||
var hasMapIndex = decode.numberCheck(bytes, it); | ||
var isSchemaType = typeof (type_1) !== "string"; | ||
var newKey = (hasMapIndex) | ||
? previousKeys[decode.number(bytes, it)] | ||
: decode.string(bytes, it); | ||
var item = void 0; | ||
var isNew = (!hasIndexChange && !valueRef[newKey]) || (hasIndexChange && previousKey === undefined && hasMapIndex); | ||
if (isNew && isSchemaType) { | ||
item = new type_1(); | ||
} | ||
else if (previousKey !== undefined) { | ||
item = valueRef[previousKey]; | ||
} | ||
else { | ||
item = valueRef[newKey]; | ||
} | ||
if (decode.nilCheck(bytes, it)) { | ||
it.offset++; | ||
if (item && item.onRemove) { | ||
item.onRemove(); | ||
} | ||
if (valueRef.onRemove) { | ||
valueRef.onRemove(item, newKey); | ||
} | ||
delete value[newKey]; | ||
continue; | ||
} | ||
else if (!isSchemaType) { | ||
value[newKey] = decodePrimitiveType(type_1, bytes, it); | ||
} | ||
else { | ||
item.decode(bytes, it); | ||
value[newKey] = item; | ||
} | ||
if (isNew) { | ||
if (valueRef.onAdd) { | ||
valueRef.onAdd(item, newKey); | ||
} | ||
} | ||
else if (valueRef.onChange) { | ||
valueRef.onChange(item, newKey); | ||
} | ||
} | ||
} | ||
else { | ||
value = decodePrimitiveType(type_1, bytes, it); | ||
hasChange = true; | ||
} | ||
if (hasChange && this_1.onChange) { | ||
changes.push({ | ||
field: field, | ||
value: change || value, | ||
previousValue: this_1["_" + field] | ||
}); | ||
} | ||
this_1["_" + field] = value; | ||
}; | ||
var this_1 = this; | ||
while (it.offset < totalBytes) { | ||
var state_1 = _loop_1(); | ||
if (state_1 === "break") | ||
break; | ||
} | ||
if (this.onChange && changes.length > 0) { | ||
this.onChange(changes); | ||
} | ||
return this; | ||
Context.prototype.has = function (schema) { | ||
return this.schemas.has(schema); | ||
}; | ||
Schema.prototype.encode = function (root, encodeAll, client) { | ||
var _this = this; | ||
if (root === void 0) { root = this; } | ||
if (encodeAll === void 0) { encodeAll = false; } | ||
var encodedBytes = []; | ||
var endStructure = function () { | ||
if (_this !== root) { | ||
encodedBytes.push(spec_1.END_OF_STRUCTURE); | ||
} | ||
}; | ||
// skip if nothing has changed | ||
if (!this.$changes.changed && !encodeAll) { | ||
endStructure(); | ||
return encodedBytes; | ||
} | ||
var schema = this._schema; | ||
var indexes = this._indexes; | ||
var filters = this._filters; | ||
var changes = (encodeAll || client) | ||
? this.$changes.allChanges | ||
: this.$changes.changes; | ||
for (var i = 0, l = changes.length; i < l; i++) { | ||
var field = changes[i]; | ||
var type_2 = schema[field]; | ||
var filter_1 = (filters && filters[field]); | ||
// const value = (filter && this.$allChanges[field]) || changes[field]; | ||
var value = this["_" + field]; | ||
var fieldIndex = indexes[field]; | ||
// skip unchagned fields | ||
if (value === undefined) { | ||
continue; | ||
} | ||
var bytes = []; | ||
if (type_2._schema) { | ||
if (client && filter_1) { | ||
// skip if not allowed by custom filter | ||
if (!filter_1.call(this, client, value, root)) { | ||
continue; | ||
} | ||
} | ||
encode.number(bytes, fieldIndex); | ||
// encode child object | ||
if (value) { | ||
assertInstanceType(value, type_2, this, field); | ||
bytes = bytes.concat(value.encode(root, encodeAll, client)); | ||
} | ||
else { | ||
// value has been removed | ||
encode.uint8(bytes, spec_1.NIL); | ||
} | ||
} | ||
else if (Array.isArray(type_2)) { | ||
encode.number(bytes, fieldIndex); | ||
// total of items in the array | ||
encode.number(bytes, value.length); | ||
var arrayChanges = (encodeAll || client) | ||
? value.$changes.allChanges | ||
: value.$changes.changes; | ||
// number of changed items | ||
encode.number(bytes, arrayChanges.length); | ||
var isChildSchema = typeof (type_2[0]) !== "string"; | ||
// assert ArraySchema was provided | ||
assertInstanceType(this["_" + field], ArraySchema_1.ArraySchema, this, field); | ||
// encode Array of type | ||
for (var j = 0; j < arrayChanges.length; j++) { | ||
var index = arrayChanges[j]; | ||
var item = this["_" + field][index]; | ||
if (client && filter_1) { | ||
// skip if not allowed by custom filter | ||
if (!filter_1.call(this, client, item, root)) { | ||
continue; | ||
} | ||
} | ||
if (isChildSchema) { // is array of Schema | ||
encode.number(bytes, index); | ||
if (item === undefined) { | ||
encode.uint8(bytes, spec_1.NIL); | ||
continue; | ||
} | ||
var indexChange = value.$changes.getIndexChange(item); | ||
if (indexChange !== undefined) { | ||
encode.uint8(bytes, spec_1.INDEX_CHANGE); | ||
encode.number(bytes, indexChange); | ||
} | ||
assertInstanceType(item, type_2[0], this, field); | ||
bytes = bytes.concat(item.encode(root, encodeAll, client)); | ||
} | ||
else { | ||
encode.number(bytes, index); | ||
assertType(item, type_2[0], this, field); | ||
if (!encodePrimitiveType(type_2[0], bytes, item)) { | ||
console.log("cannot encode", schema[field]); | ||
continue; | ||
} | ||
} | ||
} | ||
if (!encodeAll) { | ||
value.$changes.discard(); | ||
} | ||
} | ||
else if (type_2.map) { | ||
// encode Map of type | ||
encode.number(bytes, fieldIndex); | ||
// TODO: during `encodeAll`, removed entries are not going to be encoded | ||
var keys = (encodeAll || client) | ||
? value.$changes.allChanges | ||
: value.$changes.changes; | ||
encode.number(bytes, keys.length); | ||
var previousKeys = Object.keys(this["_" + field]); | ||
var isChildSchema = typeof (type_2.map) !== "string"; | ||
// assert MapSchema was provided | ||
assertInstanceType(this["_" + field], MapSchema_1.MapSchema, this, field); | ||
for (var i_1 = 0; i_1 < keys.length; i_1++) { | ||
var key = (typeof (keys[i_1]) === "number" && previousKeys[keys[i_1]]) || keys[i_1]; | ||
var item = this["_" + field][key]; | ||
var mapItemIndex = this["_" + field]._indexes.get(key); | ||
if (client && filter_1) { | ||
// skip if not allowed by custom filter | ||
if (!filter_1.call(this, client, item, root)) { | ||
continue; | ||
} | ||
} | ||
if (encodeAll) { | ||
if (item !== undefined) { | ||
mapItemIndex = undefined; | ||
} | ||
else { | ||
// previously deleted items are skipped during `encodeAll` | ||
continue; | ||
} | ||
} | ||
// encode index change | ||
var indexChange = value.$changes.getIndexChange(item); | ||
if (item && indexChange !== undefined) { | ||
encode.uint8(bytes, spec_1.INDEX_CHANGE); | ||
encode.number(bytes, this["_" + field]._indexes.get(indexChange)); | ||
} | ||
if (mapItemIndex !== undefined) { | ||
encode.number(bytes, mapItemIndex); | ||
} | ||
else { | ||
// TODO: remove item | ||
encode.string(bytes, key); | ||
} | ||
if (item && isChildSchema) { | ||
assertInstanceType(item, type_2.map, this, field); | ||
bytes = bytes.concat(item.encode(root, encodeAll, client)); | ||
} | ||
else if (item !== undefined) { | ||
assertType(item, type_2.map, this, field); | ||
encodePrimitiveType(type_2.map, bytes, item); | ||
} | ||
else { | ||
encode.uint8(bytes, spec_1.NIL); | ||
} | ||
} | ||
if (!encodeAll) { | ||
value.$changes.discard(); | ||
// TODO: track array/map indexes per client? | ||
if (!client) { | ||
this["_" + field]._updateIndexes(); | ||
} | ||
} | ||
} | ||
else { | ||
if (client && filter_1) { | ||
// skip if not allowed by custom filter | ||
if (!filter_1.call(this, client, value, root)) { | ||
continue; | ||
} | ||
} | ||
encode.number(bytes, fieldIndex); | ||
assertType(value, type_2, this, field); | ||
if (!encodePrimitiveType(type_2, bytes, value)) { | ||
console.log("cannot encode", schema[field]); | ||
continue; | ||
} | ||
} | ||
encodedBytes = encodedBytes.concat(bytes); | ||
} | ||
// flag end of Schema object structure | ||
endStructure(); | ||
if (!encodeAll && !client) { | ||
this.$changes.discard(); | ||
} | ||
return encodedBytes; | ||
Context.prototype.get = function (typeid) { | ||
return this.types[typeid]; | ||
}; | ||
Schema.prototype.encodeFiltered = function (client) { | ||
return this.encode(this, false, client); | ||
Context.prototype.add = function (schema) { | ||
schema._typeid = this.schemas.size; | ||
this.types[schema._typeid] = schema; | ||
this.schemas.set(schema, schema._typeid); | ||
}; | ||
Schema.prototype.encodeAll = function () { | ||
return this.encode(this, true); | ||
}; | ||
Schema.prototype.encodeAllFiltered = function (client) { | ||
return this.encode(this, true, client); | ||
}; | ||
Schema.prototype.clone = function () { | ||
var cloned = new (this.constructor); | ||
var schema = this._schema; | ||
for (var field in schema) { | ||
cloned[field] = this[field]; | ||
} | ||
return cloned; | ||
}; | ||
Schema.prototype.triggerAll = function () { | ||
if (!this.onChange) { | ||
return; | ||
} | ||
var changes = []; | ||
var schema = this._schema; | ||
for (var field in schema) { | ||
if (this[field] !== undefined) { | ||
changes.push({ | ||
field: field, | ||
value: this[field], | ||
previousValue: undefined | ||
}); | ||
} | ||
} | ||
this.onChange(changes); | ||
}; | ||
Schema.prototype.toJSON = function () { | ||
var schema = this._schema; | ||
var obj = {}; | ||
for (var field in schema) { | ||
obj[field] = this["_" + field]; | ||
} | ||
return obj; | ||
}; | ||
return Schema; | ||
return Context; | ||
}()); | ||
exports.Schema = Schema; | ||
exports.Context = Context; | ||
exports.globalContext = new Context(); | ||
/** | ||
* Reflection | ||
*/ | ||
var ReflectionField = /** @class */ (function (_super) { | ||
__extends(ReflectionField, _super); | ||
function ReflectionField() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
__decorate([ | ||
type("string") | ||
], ReflectionField.prototype, "name", void 0); | ||
__decorate([ | ||
type("string") | ||
], ReflectionField.prototype, "type", void 0); | ||
__decorate([ | ||
type("uint8") | ||
], ReflectionField.prototype, "referencedType", void 0); | ||
return ReflectionField; | ||
}(Schema)); | ||
exports.ReflectionField = ReflectionField; | ||
var ReflectionType = /** @class */ (function (_super) { | ||
__extends(ReflectionType, _super); | ||
function ReflectionType() { | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.fields = new ArraySchema_1.ArraySchema(); | ||
return _this; | ||
} | ||
__decorate([ | ||
type("uint8") | ||
], ReflectionType.prototype, "id", void 0); | ||
__decorate([ | ||
type([ReflectionField]) | ||
], ReflectionType.prototype, "fields", void 0); | ||
return ReflectionType; | ||
}(Schema)); | ||
exports.ReflectionType = ReflectionType; | ||
var Reflection = /** @class */ (function (_super) { | ||
__extends(Reflection, _super); | ||
function Reflection() { | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.types = new ArraySchema_1.ArraySchema(); | ||
return _this; | ||
} | ||
Reflection.encode = function (instance) { | ||
var reflection = new Reflection(); | ||
var schema = instance._schema; | ||
var lastTypeId = 0; | ||
var rootType = new ReflectionType(); | ||
rootType.id = lastTypeId++; | ||
var typeIds = {}; | ||
var buildType = function (currentType, schema) { | ||
for (var fieldName in schema) { | ||
var field = new ReflectionField(); | ||
field.name = fieldName; | ||
var fieldType = void 0; | ||
if (typeof (schema[fieldName]) === "string") { | ||
fieldType = schema[fieldName]; | ||
} | ||
else { | ||
var isSchema = typeof (schema[fieldName]) === "function"; | ||
var isArray = Array.isArray(schema[fieldName]); | ||
var isMap = !isArray && schema[fieldName].map; | ||
var childTypeSchema = void 0; | ||
if (isSchema) { | ||
fieldType = "ref"; | ||
childTypeSchema = schema[fieldName]; | ||
} | ||
else if (isArray) { | ||
fieldType = "array"; | ||
if (typeof (schema[fieldName][0]) === "string") { | ||
fieldType += ":" + schema[fieldName][0]; // array:string | ||
} | ||
else { | ||
childTypeSchema = schema[fieldName][0]; | ||
} | ||
} | ||
else if (isMap) { | ||
fieldType = "map"; | ||
if (typeof (schema[fieldName].map) === "string") { | ||
fieldType += ":" + schema[fieldName].map; // array:string | ||
} | ||
else { | ||
childTypeSchema = schema[fieldName].map; | ||
} | ||
} | ||
if (childTypeSchema) { | ||
var childSchemaName = childTypeSchema.name; | ||
if (typeIds[childSchemaName] === undefined) { | ||
var childType = new ReflectionType(); | ||
childType.id = lastTypeId++; | ||
typeIds[childSchemaName] = childType.id; | ||
buildType(childType, childTypeSchema._schema); | ||
} | ||
field.referencedType = typeIds[childSchemaName]; | ||
} | ||
else { | ||
field.referencedType = 255; | ||
} | ||
} | ||
field.type = fieldType; | ||
currentType.fields.push(field); | ||
} | ||
reflection.types.push(currentType); | ||
}; | ||
buildType(rootType, schema); | ||
return reflection.encodeAll(); | ||
}; | ||
Reflection.decode = function (bytes) { | ||
var reflection = new Reflection(); | ||
reflection.decode(bytes); | ||
var schemaTypes = reflection.types.reduce(function (types, reflectionType) { | ||
types[reflectionType.id] = /** @class */ (function (_super) { | ||
__extends(_, _super); | ||
function _() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
return _; | ||
}(Schema)); | ||
return types; | ||
}, {}); | ||
reflection.types.forEach(function (reflectionType, i) { | ||
reflectionType.fields.forEach(function (field) { | ||
var schemaType = schemaTypes[reflectionType.id]; | ||
if (field.referencedType !== undefined) { | ||
var refType = schemaTypes[field.referencedType]; | ||
// map or array of primitive type (255) | ||
if (!refType) { | ||
refType = field.type.split(":")[1]; | ||
} | ||
if (field.type.indexOf("array") === 0) { | ||
type([refType])(schemaType.prototype, field.name); | ||
} | ||
else if (field.type.indexOf("map") === 0) { | ||
type({ map: refType })(schemaType.prototype, field.name); | ||
} | ||
else if (field.type === "ref") { | ||
type(refType)(schemaType.prototype, field.name); | ||
} | ||
} | ||
else { | ||
type(field.type)(schemaType.prototype, field.name); | ||
} | ||
}); | ||
}); | ||
var rootType = schemaTypes[0]; | ||
var rootInstance = new rootType(); | ||
/** | ||
* auto-initialize referenced types on root type | ||
* to allow registering listeners immediatelly on client-side | ||
*/ | ||
for (var fieldName in rootType._schema) { | ||
var fieldType = rootType._schema[fieldName]; | ||
if (typeof (fieldType) !== "string") { | ||
var isSchema = typeof (fieldType) === "function"; | ||
var isArray = Array.isArray(fieldType); | ||
var isMap = !isArray && fieldType.map; | ||
rootInstance[fieldName] = (isArray) | ||
? new ArraySchema_1.ArraySchema() | ||
: (isMap) | ||
? new MapSchema_1.MapSchema() | ||
: (isSchema) | ||
? new fieldType() | ||
: undefined; | ||
} | ||
} | ||
return rootInstance; | ||
}; | ||
__decorate([ | ||
type([ReflectionType]) | ||
], Reflection.prototype, "types", void 0); | ||
return Reflection; | ||
}(Schema)); | ||
exports.Reflection = Reflection; | ||
/** | ||
* `@type()` decorator for proxies | ||
*/ | ||
function type(type) { | ||
function type(type, context) { | ||
if (context === void 0) { context = exports.globalContext; } | ||
return function (target, field) { | ||
var constructor = target.constructor; | ||
constructor._context = context; | ||
/* | ||
* static schema | ||
*/ | ||
if (!definedSchemas.get(constructor)) { | ||
definedSchemas.set(constructor, true); | ||
if (!context.has(constructor)) { | ||
context.add(constructor); | ||
// support inheritance | ||
@@ -783,3 +77,3 @@ constructor._schema = Object.assign({}, constructor._schema || {}); | ||
obj.$changes.mapIndex(setValue, key); | ||
if (setValue instanceof Schema) { | ||
if (setValue instanceof Schema_1.Schema) { | ||
// new items are flagged with all changes | ||
@@ -827,3 +121,3 @@ if (!setValue.$changes.parent) { | ||
for (var i = 0; i < value.length; i++) { | ||
if (value[i] instanceof Schema) { | ||
if (value[i] instanceof Schema_1.Schema) { | ||
value[i].$changes = new ChangeTree_1.ChangeTree(i, value.$changes); | ||
@@ -841,3 +135,3 @@ value[i].$changes.changeAll(value[i]); | ||
for (var key in value) { | ||
if (value[key] instanceof Schema) { | ||
if (value[key] instanceof Schema_1.Schema) { | ||
value[key].$changes = new ChangeTree_1.ChangeTree(key, value.$changes); | ||
@@ -844,0 +138,0 @@ value[key].$changes.changeAll(value[key]); |
@@ -1,2 +0,2 @@ | ||
import { Schema } from "./annotations"; | ||
import { Schema } from "./Schema"; | ||
import { ArraySchema } from "./types/ArraySchema"; | ||
@@ -3,0 +3,0 @@ import { MapSchema } from "./types/MapSchema"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var annotations_1 = require("./annotations"); | ||
var Schema_1 = require("./Schema"); | ||
var ArraySchema_1 = require("./types/ArraySchema"); | ||
@@ -62,3 +62,3 @@ var MapSchema_1 = require("./types/MapSchema"); | ||
ChangeTree.prototype.changeAll = function (obj) { | ||
if (obj instanceof annotations_1.Schema) { | ||
if (obj instanceof Schema_1.Schema) { | ||
var schema = obj._schema; | ||
@@ -68,3 +68,3 @@ for (var field in schema) { | ||
// on its structure have a valid parent. | ||
if ((obj[field] instanceof annotations_1.Schema || | ||
if ((obj[field] instanceof Schema_1.Schema || | ||
obj[field] instanceof ArraySchema_1.ArraySchema || | ||
@@ -71,0 +71,0 @@ obj[field] instanceof MapSchema_1.MapSchema) && |
@@ -0,3 +1,5 @@ | ||
export { Schema, DataChange } from "./Schema"; | ||
export { MapSchema } from "./types/MapSchema"; | ||
export { ArraySchema } from "./types/ArraySchema"; | ||
export { Schema, type, filter, DataChange, PrimitiveType, Definition, DefinitionType, FilterCallback, Reflection, ReflectionType, ReflectionField, } from "./annotations"; | ||
export { Reflection, ReflectionType, ReflectionField, } from "./Reflection"; | ||
export { type, filter, Context, PrimitiveType, Definition, DefinitionType, FilterCallback, } from "./annotations"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Schema_1 = require("./Schema"); | ||
exports.Schema = Schema_1.Schema; | ||
var MapSchema_1 = require("./types/MapSchema"); | ||
@@ -7,10 +9,12 @@ exports.MapSchema = MapSchema_1.MapSchema; | ||
exports.ArraySchema = ArraySchema_1.ArraySchema; | ||
// Reflection | ||
var Reflection_1 = require("./Reflection"); | ||
exports.Reflection = Reflection_1.Reflection; | ||
exports.ReflectionType = Reflection_1.ReflectionType; | ||
exports.ReflectionField = Reflection_1.ReflectionField; | ||
var annotations_1 = require("./annotations"); | ||
// Public API | ||
exports.Schema = annotations_1.Schema; | ||
// Annotations | ||
exports.type = annotations_1.type; | ||
exports.filter = annotations_1.filter; | ||
// Reflection | ||
exports.Reflection = annotations_1.Reflection; | ||
exports.ReflectionType = annotations_1.ReflectionType; | ||
exports.ReflectionField = annotations_1.ReflectionField; | ||
// Types | ||
exports.Context = annotations_1.Context; |
export declare const END_OF_STRUCTURE = 193; | ||
export declare const NIL = 192; | ||
export declare const INDEX_CHANGE = 212; | ||
export declare const TYPE_ID = 213; |
@@ -6,1 +6,2 @@ "use strict"; | ||
exports.INDEX_CHANGE = 0xd4; | ||
exports.TYPE_ID = 0xd5; |
{ | ||
"name": "@colyseus/schema", | ||
"version": "0.4.24", | ||
"version": "0.4.25-alpha.0", | ||
"description": "Schema-based binary serializer / de-serializer.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
108449
40
2592