@colyseus/schema
Advanced tools
Comparing version 0.1.8 to 0.3.0
import * as decode from "./msgpack/decode"; | ||
export declare type PrimitiveType = "string" | "number" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | typeof Schema; | ||
import { ArraySchema } from './types/ArraySchema'; | ||
/** | ||
* Data types | ||
*/ | ||
export declare type PrimitiveType = "string" | "number" | "boolean" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" | typeof Schema; | ||
export declare type DefinitionType = (PrimitiveType | PrimitiveType[] | { | ||
@@ -9,7 +13,10 @@ map: PrimitiveType; | ||
}; | ||
export interface DataChange { | ||
export interface DataChange<T = any> { | ||
field: string; | ||
value: any; | ||
previousValue: any; | ||
value: T; | ||
previousValue: T; | ||
} | ||
/** | ||
* Schema encoder / decoder | ||
*/ | ||
export declare abstract class Schema { | ||
@@ -20,2 +27,5 @@ static _schema: Definition; | ||
}; | ||
protected $allChanges: { | ||
[key: string]: any; | ||
}; | ||
protected $changes: { | ||
@@ -37,4 +47,26 @@ [key: string]: any; | ||
decode(bytes: any, it?: decode.Iterator): this; | ||
encode(root?: boolean, encodedBytes?: any[]): any[]; | ||
encode(root?: boolean, encodeAll?: boolean): any[]; | ||
encodeAll(): any[]; | ||
toJSON(): {}; | ||
} | ||
/** | ||
* Reflection | ||
*/ | ||
export declare class ReflectionField extends Schema { | ||
name: string; | ||
type: PrimitiveType; | ||
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[]): any; | ||
} | ||
/** | ||
* Decorators / Proxies | ||
*/ | ||
export declare function type(type: DefinitionType): (target: any, field: string) => void; |
"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 }); | ||
@@ -6,2 +25,4 @@ var spec_1 = require("./spec"); | ||
var decode = require("./msgpack/decode"); | ||
var ArraySchema_1 = require("./types/ArraySchema"); | ||
var MapSchema_1 = require("./types/MapSchema"); | ||
function encodePrimitiveType(type, bytes, value) { | ||
@@ -26,2 +47,5 @@ var encodeFunc = encode[type]; | ||
} | ||
/** | ||
* Schema encoder / decoder | ||
*/ | ||
var Schema = /** @class */ (function () { | ||
@@ -34,2 +58,3 @@ // allow inherited classes to have a constructor | ||
} | ||
this.$allChanges = {}; | ||
this.$changes = {}; | ||
@@ -41,3 +66,3 @@ this.$changed = false; | ||
this.$changed = true; | ||
if (value) { | ||
if (value !== undefined) { | ||
if (Array.isArray(value.$parentField) || | ||
@@ -56,7 +81,14 @@ fieldSchema && (Array.isArray(fieldSchema) || fieldSchema.map)) { | ||
} | ||
if (fieldKey !== undefined && | ||
this.$changes[fieldName].indexOf(fieldKey) === -1 // do not store duplicates of changed fields | ||
) { | ||
this.$changes[fieldName].push(fieldKey); | ||
if (!this.$allChanges[fieldName]) { | ||
this.$allChanges[fieldName] = []; | ||
} | ||
if (fieldKey !== undefined) { | ||
// do not store duplicates of changed fields | ||
if (this.$changes[fieldName].indexOf(fieldKey) === -1) { | ||
this.$changes[fieldName].push(fieldKey); | ||
} | ||
if (this.$allChanges[fieldName].indexOf(fieldKey) === -1) { | ||
this.$allChanges[fieldName].push(fieldKey); | ||
} | ||
} | ||
} | ||
@@ -66,2 +98,3 @@ else if (value.$parentField) { | ||
this.$changes[value.$parentField] = value; | ||
this.$allChanges[value.$parentField] = value; | ||
} | ||
@@ -71,2 +104,3 @@ else { | ||
this.$changes[field] = this["_" + field]; | ||
this.$allChanges[field] = this["_" + field]; | ||
} | ||
@@ -103,8 +137,7 @@ } | ||
var totalBytes = bytes.length; | ||
while (it.offset < totalBytes) { | ||
var _loop_1 = function () { | ||
var index = bytes[it.offset++]; | ||
var field = fieldsByIndex[index]; | ||
if (index === spec_1.END_OF_STRUCTURE) { | ||
// reached end of strucutre. skip. | ||
break; | ||
return "break"; | ||
} | ||
@@ -116,4 +149,4 @@ var type_1 = schema[field]; | ||
if (type_1._schema) { | ||
value = this["_" + field] || new type_1(); | ||
value.$parent = this; | ||
value = this_1["_" + field] || new type_1(); | ||
value.$parent = this_1; | ||
value.decode(bytes, it); | ||
@@ -125,5 +158,5 @@ hasChange = true; | ||
change = []; | ||
var valueRef = this["_" + field] || []; | ||
value = valueRef.slice(0); | ||
var newLength = decode.number(bytes, it); | ||
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); | ||
@@ -135,7 +168,10 @@ hasChange = (numChanges > 0); | ||
// ensure current array has the same length as encoded one | ||
if (value.length > newLength) { | ||
value.splice(newLength).forEach(function (itemRemoved) { | ||
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); | ||
} | ||
}); | ||
@@ -145,8 +181,2 @@ } | ||
var newIndex = decode.number(bytes, it); | ||
if (decode.nilCheck(bytes, it)) { | ||
// const item = this[`_${field}`][newIndex]; | ||
// TODO: trigger `onRemove` on Schema object being removed. | ||
it.offset++; | ||
continue; | ||
} | ||
// index change check | ||
@@ -161,16 +191,28 @@ var indexChangedFrom = void 0; | ||
var item = void 0; | ||
if (hasIndexChange && indexChangedFrom === undefined && newIndex !== undefined) { | ||
var isNew = (hasIndexChange && indexChangedFrom === undefined && newIndex !== undefined); | ||
if (isNew) { | ||
item = new type_1(); | ||
} | ||
else if (indexChangedFrom !== undefined) { | ||
item = valueRef[indexChangedFrom]; | ||
item = valueRef_1[indexChangedFrom]; | ||
} | ||
else if (newIndex !== undefined) { | ||
item = valueRef[newIndex]; | ||
item = valueRef_1[newIndex]; | ||
} | ||
if (!item) { | ||
item = new type_1(); | ||
isNew = true; | ||
} | ||
item.$parent = this; | ||
if (decode.nilCheck(bytes, it)) { | ||
it.offset++; | ||
if (valueRef_1.onRemove) { | ||
valueRef_1.onRemove(item, newIndex); | ||
} | ||
continue; | ||
} | ||
item.$parent = this_1; | ||
item.decode(bytes, it); | ||
if (isNew && valueRef_1.onAdd) { | ||
valueRef_1.onAdd(item, newIndex); | ||
} | ||
value[newIndex] = item; | ||
@@ -186,4 +228,4 @@ } | ||
type_1 = type_1.map; | ||
var valueRef = this["_" + field] || {}; | ||
value = Object.assign({}, valueRef); | ||
var valueRef = this_1["_" + field] || new MapSchema_1.MapSchema(); | ||
value = valueRef.clone(); | ||
var length = decode.number(bytes, it); | ||
@@ -194,3 +236,10 @@ hasChange = (length > 0); | ||
var hasIndexChange = false; | ||
var mapKeys = 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 | ||
@@ -200,3 +249,3 @@ var previousKey = void 0; | ||
decode.uint8(bytes, it); | ||
previousKey = Object.keys(valueRef)[decode.number(bytes, it)]; | ||
previousKey = mapKeys[decode.number(bytes, it)]; | ||
hasIndexChange = true; | ||
@@ -206,5 +255,6 @@ } | ||
var newKey = (hasMapIndex) | ||
? Object.keys(valueRef)[decode.number(bytes, it)] | ||
? mapKeys[decode.number(bytes, it)] | ||
: decode.string(bytes, it); | ||
var item = void 0; | ||
var isNew = (hasIndexChange && previousKey === undefined && hasMapIndex); | ||
if (hasIndexChange && previousKey === undefined && hasMapIndex) { | ||
@@ -221,2 +271,3 @@ item = new type_1(); | ||
item = new type_1(); | ||
isNew = true; | ||
} | ||
@@ -228,2 +279,5 @@ if (decode.nilCheck(bytes, it)) { | ||
} | ||
if (valueRef.onRemove) { | ||
valueRef.onRemove(item, newKey); | ||
} | ||
delete value[newKey]; | ||
@@ -236,5 +290,11 @@ continue; | ||
else { | ||
item.$parent = this; | ||
item.$parent = this_1; | ||
item.decode(bytes, it); | ||
value[newKey] = item; | ||
if (isNew && valueRef.onAdd) { | ||
valueRef.onAdd(item, newKey); | ||
} | ||
else if (valueRef.onChange) { | ||
valueRef.onChange(item, newKey); | ||
} | ||
} | ||
@@ -247,11 +307,16 @@ } | ||
} | ||
if (this.onChange && hasChange) { | ||
if (hasChange && this_1.onChange) { | ||
changes.push({ | ||
field: field, | ||
value: change || value, | ||
// value: value, | ||
previousValue: this["_" + field] | ||
previousValue: this_1["_" + field] | ||
}); | ||
} | ||
this["_" + field] = value; | ||
this_1["_" + field] = value; | ||
}; | ||
var this_1 = this; | ||
while (it.offset < totalBytes) { | ||
var state_1 = _loop_1(); | ||
if (state_1 === "break") | ||
break; | ||
} | ||
@@ -263,5 +328,6 @@ if (this.onChange && changes.length > 0) { | ||
}; | ||
Schema.prototype.encode = function (root, encodedBytes) { | ||
Schema.prototype.encode = function (root, encodeAll) { | ||
if (root === void 0) { root = true; } | ||
if (encodedBytes === void 0) { encodedBytes = []; } | ||
if (encodeAll === void 0) { encodeAll = false; } | ||
var encodedBytes = []; | ||
var endStructure = function () { | ||
@@ -273,3 +339,3 @@ if (!root) { | ||
// skip if nothing has changed | ||
if (!this.$changed) { | ||
if (!this.$changed && !encodeAll) { | ||
endStructure(); | ||
@@ -280,6 +346,9 @@ return encodedBytes; | ||
var indexes = this._indexes; | ||
for (var field in this.$changes) { | ||
var changes = (encodeAll) | ||
? this.$allChanges | ||
: this.$changes; | ||
for (var field in changes) { | ||
var bytes = []; | ||
var type_2 = schema[field]; | ||
var value = this.$changes[field]; | ||
var value = changes[field]; | ||
var fieldIndex = indexes[field]; | ||
@@ -293,3 +362,3 @@ // skip unchagned fields | ||
// encode child object | ||
bytes = bytes.concat(value.encode(false)); | ||
bytes = bytes.concat(value.encode(false, encodeAll)); | ||
// ensure parent is set | ||
@@ -327,3 +396,3 @@ // in case it was manually instantiated | ||
} | ||
bytes = bytes.concat(item.encode(false)); | ||
bytes = bytes.concat(item.encode(false, encodeAll)); | ||
} | ||
@@ -340,10 +409,20 @@ else { | ||
else if (type_2.map) { | ||
// encode Map of type | ||
encode.number(bytes, fieldIndex); | ||
// encode Map of type | ||
var keys = value; | ||
var keys = value; // TODO: during `encodeAll`, removed entries are not going to be encoded | ||
encode.number(bytes, keys.length); | ||
var mapKeys = Object.keys(this["_" + field]); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = keys[i]; | ||
var key = mapKeys[keys[i]] || keys[i]; | ||
var item = this["_" + field][key]; | ||
var mapItemIndex = this["_" + field + "MapIndex"][key]; | ||
var mapItemIndex = this["_" + field]._indexes[key]; | ||
if (encodeAll) { | ||
if (item) { | ||
mapItemIndex = undefined; | ||
} | ||
else { | ||
// previously deleted items are skipped during `encodeAll` | ||
continue; | ||
} | ||
} | ||
// encode index change | ||
@@ -356,13 +435,16 @@ if (item && item.$parentIndexChange >= 0) { | ||
if (mapItemIndex !== undefined) { | ||
key = mapItemIndex; | ||
encode.number(bytes, key); | ||
encode.number(bytes, mapItemIndex); | ||
} | ||
else { | ||
// TODO: remove item | ||
encode.string(bytes, key); | ||
this["_" + field + "MapIndex"][key] = Object.keys(this["_" + field]).indexOf(key); | ||
var mapKey = mapKeys.indexOf(key); | ||
if (mapKey >= 0) { | ||
this["_" + field]._indexes[key] = mapKey; | ||
} | ||
} | ||
if (item instanceof Schema) { | ||
item.$parent = this; | ||
item.$parentField = [field, key]; | ||
bytes = bytes.concat(item.encode(false)); | ||
item.$parentField = [field, keys[i]]; | ||
bytes = bytes.concat(item.encode(false, encodeAll)); | ||
} | ||
@@ -376,2 +458,3 @@ else if (item !== undefined) { | ||
} | ||
this["_" + field]._updateIndexes(); | ||
} | ||
@@ -393,5 +476,168 @@ else { | ||
}; | ||
Schema.prototype.encodeAll = function () { | ||
return this.encode(true, true); | ||
}; | ||
Schema.prototype.toJSON = function () { | ||
var schema = this._schema; | ||
var obj = {}; | ||
for (var field in schema) { | ||
obj[field] = this["_" + field]; | ||
} | ||
return obj; | ||
}; | ||
return Schema; | ||
}()); | ||
exports.Schema = Schema; | ||
/** | ||
* 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("number") | ||
], 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]); | ||
// const isMap = !isArray && (schema[fieldName] as any).map; | ||
fieldType = (isArray) | ||
? "array" | ||
: (isSchema) | ||
? "ref" | ||
: "map"; | ||
var childSchema = (isArray) | ||
? schema[fieldName][0] | ||
: (isSchema) | ||
? schema[fieldName] | ||
: schema[fieldName].map; | ||
var childSchemaName = childSchema.name; | ||
if (typeIds[childSchemaName] === undefined) { | ||
var childType = new ReflectionType(); | ||
childType.id = lastTypeId++; | ||
typeIds[childSchemaName] = childType.id; | ||
buildType(childType, (new childSchema())._schema); | ||
} | ||
field.referencedType = typeIds[childSchemaName]; | ||
} | ||
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.reverse().map(function (_) { | ||
return /** @class */ (function (_super) { | ||
__extends(_, _super); | ||
function _() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
return _; | ||
}(Schema)); | ||
}); | ||
reflection.types.forEach(function (reflectionType, i) { | ||
reflectionType.fields.forEach(function (field) { | ||
var schemaType = schemaTypes[i]; | ||
if (field.referencedType !== undefined) { | ||
var refType = schemaTypes[field.referencedType]; | ||
if (field.type === "array") { | ||
type([refType])(schemaType.prototype, field.name); | ||
} | ||
else if (field.type === "map") { | ||
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 = new schemaTypes[0]; | ||
/** | ||
* 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; | ||
rootType[fieldName] = (isArray) | ||
? new ArraySchema_1.ArraySchema() | ||
: (isMap) | ||
? new MapSchema_1.MapSchema() | ||
: (isSchema) | ||
? new fieldType() | ||
: undefined; | ||
} | ||
} | ||
return rootType; | ||
}; | ||
__decorate([ | ||
type([ReflectionType]) | ||
], Reflection.prototype, "types", void 0); | ||
return Reflection; | ||
}(Schema)); | ||
exports.Reflection = Reflection; | ||
/** | ||
* Decorators / Proxies | ||
*/ | ||
function type(type) { | ||
@@ -409,2 +655,6 @@ return function (target, field) { | ||
constructor._schema[field] = type; | ||
/** | ||
* TODO: `isSchema` / `isArray` / `isMap` is repeated on many places! | ||
* need to refactor all of them. | ||
*/ | ||
var isArray = Array.isArray(type); | ||
@@ -428,5 +678,2 @@ var isMap = !isArray && type.map; | ||
if (isArray || isMap) { | ||
if (isMap) { | ||
this[fieldCached + "MapIndex"] = {}; | ||
} | ||
value = new Proxy(value, { | ||
@@ -437,6 +684,6 @@ get: function (obj, prop) { return obj[prop]; }, | ||
// ensure new value has a parent | ||
var key = (isArray) ? Number(prop) : prop; | ||
var key = (isArray) ? Number(prop) : String(prop); | ||
if (setValue.$parentField && setValue.$parentField[1] !== key) { | ||
if (isMap) { | ||
var indexChange = _this[fieldCached + "MapIndex"][setValue.$parentField[1]]; | ||
var indexChange = _this["" + fieldCached]._indexes[setValue.$parentField[1]]; | ||
setValue.$parentIndexChange = indexChange; | ||
@@ -455,3 +702,2 @@ } | ||
obj[prop] = setValue; | ||
// console.log("setValue:", obj, setValue) | ||
_this.markAsChanged(field, obj); | ||
@@ -471,3 +717,3 @@ } | ||
// ensure new value has a parent | ||
if (previousValue.$parent) { | ||
if (previousValue && previousValue.$parent) { | ||
previousValue.$parent.markAsChanged(field, previousValue); | ||
@@ -474,0 +720,0 @@ } |
@@ -1,1 +0,3 @@ | ||
export { Schema, type, DataChange, PrimitiveType, Definition, DefinitionType, } from "./annotations"; | ||
export { MapSchema } from "./types/MapSchema"; | ||
export { ArraySchema } from "./types/ArraySchema"; | ||
export { Schema, type, DataChange, PrimitiveType, Definition, DefinitionType, Reflection, ReflectionType, ReflectionField, } from "./annotations"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var MapSchema_1 = require("./types/MapSchema"); | ||
exports.MapSchema = MapSchema_1.MapSchema; | ||
var ArraySchema_1 = require("./types/ArraySchema"); | ||
exports.ArraySchema = ArraySchema_1.ArraySchema; | ||
var annotations_1 = require("./annotations"); | ||
// Public API | ||
exports.Schema = annotations_1.Schema; | ||
exports.type = annotations_1.type; | ||
// Reflection | ||
exports.Reflection = annotations_1.Reflection; | ||
exports.ReflectionType = annotations_1.ReflectionType; | ||
exports.ReflectionField = annotations_1.ReflectionField; |
@@ -36,5 +36,7 @@ /** | ||
export declare function uint32(bytes: number[], it: Iterator): number; | ||
export declare function float32(bytes: number[], it: Iterator): number; | ||
export declare function float64(bytes: number[], it: Iterator): number; | ||
export declare function readFloat32(bytes: number[], it: Iterator): number; | ||
export declare function readFloat64(bytes: number[], it: Iterator): number; | ||
/****/ | ||
export declare function boolean(bytes: number[], it: Iterator): boolean; | ||
export declare function string(bytes: any, it: Iterator): string; | ||
@@ -47,5 +49,1 @@ export declare function stringCheck(bytes: any, it: Iterator): boolean; | ||
export declare function indexChangeCheck(bytes: any, it: Iterator): boolean; | ||
/** | ||
* UNUSED. LEFT HERE JUST FOR REFERENCE. | ||
*/ | ||
export declare function decode(bytes: any, it: Iterator): any; |
@@ -77,2 +77,10 @@ "use strict"; | ||
; | ||
function float32(bytes, it) { | ||
return readFloat32(bytes, it); | ||
} | ||
exports.float32 = float32; | ||
function float64(bytes, it) { | ||
return readFloat64(bytes, it); | ||
} | ||
exports.float64 = float64; | ||
// export function int64 (bytes: number[], it: Iterator) { | ||
@@ -101,3 +109,7 @@ // return new flatbuffers.Long(int32(bytes, it), int32(bytes, it)); | ||
; | ||
/****/ | ||
function boolean(bytes, it) { | ||
return uint8(bytes, it) > 0; | ||
} | ||
exports.boolean = boolean; | ||
; | ||
function string(bytes, it) { | ||
@@ -225,177 +237,1 @@ var prefix = bytes[it.offset++]; | ||
exports.indexChangeCheck = indexChangeCheck; | ||
/** | ||
* UNUSED. LEFT HERE JUST FOR REFERENCE. | ||
*/ | ||
function decode(bytes, it) { | ||
var prefix = bytes[it.offset++]; | ||
var value, length = 0, type = 0, hi = 0, lo = 0; | ||
if (prefix < 0xc0) { | ||
// positive fixint | ||
if (prefix < 0x80) { | ||
return prefix; | ||
} | ||
// fixmap | ||
if (prefix < 0x90) { | ||
return this._map(prefix & 0x0f); | ||
} | ||
// fixarray | ||
if (prefix < 0xa0) { | ||
return this._array(prefix & 0x0f); | ||
} | ||
// fixstr | ||
return _str(bytes, it, prefix & 0x1f); | ||
} | ||
// negative fixint | ||
if (prefix > 0xdf) { | ||
return (0xff - prefix + 1) * -1; | ||
} | ||
switch (prefix) { | ||
// nil | ||
case 0xc0: | ||
return null; | ||
// false | ||
case 0xc2: | ||
return false; | ||
// true | ||
case 0xc3: | ||
return true; | ||
// bin | ||
case 0xc4: | ||
length = bytes[it.offset]; | ||
it.offset += 1; | ||
return this._bin(length); | ||
case 0xc5: | ||
length = bytes[it.offset]; | ||
it.offset += 2; | ||
return this._bin(length); | ||
case 0xc6: | ||
length = bytes[it.offset]; | ||
it.offset += 4; | ||
return this._bin(length); | ||
// ext | ||
case 0xc7: | ||
length = bytes[it.offset]; | ||
type = bytes[it.offset + 1]; | ||
it.offset += 2; | ||
return [type, this._bin(length)]; | ||
case 0xc8: | ||
length = bytes[it.offset]; | ||
type = bytes[it.offset + 2]; | ||
it.offset += 3; | ||
return [type, this._bin(length)]; | ||
case 0xc9: | ||
length = bytes[it.offset]; | ||
type = bytes[it.offset + 4]; | ||
it.offset += 5; | ||
return [type, this._bin(length)]; | ||
// float | ||
case 0xca: | ||
value = bytes[it.offset]; | ||
it.offset += 4; | ||
return value; | ||
case 0xcb: | ||
value = bytes[it.offset]; | ||
it.offset += 8; | ||
return value; | ||
// uint | ||
case 0xcc: | ||
value = bytes[it.offset]; | ||
it.offset += 1; | ||
return value; | ||
case 0xcd: | ||
value = bytes[it.offset]; | ||
it.offset += 2; | ||
return value; | ||
case 0xce: | ||
value = bytes[it.offset]; | ||
it.offset += 4; | ||
return value; | ||
case 0xcf: | ||
hi = bytes[it.offset] * Math.pow(2, 32); | ||
lo = bytes[it.offset + 4]; | ||
it.offset += 8; | ||
return hi + lo; | ||
// int | ||
case 0xd0: | ||
value = bytes[it.offset]; | ||
it.offset += 1; | ||
return value; | ||
case 0xd1: | ||
value = bytes[it.offset]; | ||
it.offset += 2; | ||
return value; | ||
case 0xd2: | ||
value = bytes[it.offset]; | ||
it.offset += 4; | ||
return value; | ||
case 0xd3: | ||
hi = bytes[it.offset] * Math.pow(2, 32); | ||
lo = bytes[it.offset + 4]; | ||
it.offset += 8; | ||
return hi + lo; | ||
// fixext | ||
case 0xd4: | ||
type = bytes[it.offset]; | ||
it.offset += 1; | ||
if (type === 0x00) { | ||
it.offset += 1; | ||
return void 0; | ||
} | ||
return [type, this._bin(1)]; | ||
case 0xd5: | ||
type = bytes[it.offset]; | ||
it.offset += 1; | ||
return [type, this._bin(2)]; | ||
case 0xd6: | ||
type = bytes[it.offset]; | ||
it.offset += 1; | ||
return [type, this._bin(4)]; | ||
case 0xd7: | ||
type = bytes[it.offset]; | ||
it.offset += 1; | ||
if (type === 0x00) { | ||
hi = bytes[it.offset] * Math.pow(2, 32); | ||
lo = bytes[it.offset + 4]; | ||
it.offset += 8; | ||
return new Date(hi + lo); | ||
} | ||
return [type, this._bin(8)]; | ||
case 0xd8: | ||
type = bytes[it.offset]; | ||
it.offset += 1; | ||
return [type, this._bin(16)]; | ||
// str | ||
case 0xd9: | ||
length = bytes[it.offset]; | ||
it.offset += 1; | ||
return this._str(length); | ||
case 0xda: | ||
length = bytes[it.offset]; | ||
it.offset += 2; | ||
return this._str(length); | ||
case 0xdb: | ||
length = bytes[it.offset]; | ||
it.offset += 4; | ||
return this._str(length); | ||
// array | ||
case 0xdc: | ||
length = bytes[it.offset]; | ||
it.offset += 2; | ||
return this._array(length); | ||
case 0xdd: | ||
length = bytes[it.offset]; | ||
it.offset += 4; | ||
return this._array(length); | ||
// map | ||
case 0xde: | ||
length = bytes[it.offset]; | ||
it.offset += 2; | ||
return this._map(length); | ||
case 0xdf: | ||
length = bytes[it.offset]; | ||
it.offset += 4; | ||
return this._map(length); | ||
} | ||
throw new Error('Could not parse'); | ||
} | ||
exports.decode = decode; |
@@ -32,9 +32,8 @@ /** | ||
export declare function uint64(bytes: any, value: any): void; | ||
export declare function float32(bytes: any, value: any): void; | ||
export declare function float64(bytes: any, value: any): void; | ||
export declare function writeFloat32(bytes: any, value: any): void; | ||
export declare function writeFloat64(bytes: any, value: any): void; | ||
export declare function boolean(bytes: any, value: any): void; | ||
export declare function string(bytes: any, value: any): number; | ||
export declare function number(bytes: any, value: any): 1 | 2 | 3 | 5 | 9; | ||
/** | ||
* UNUSED. LEFT HERE JUST FOR REFERENCE. | ||
*/ | ||
export declare function encode(bytes: any, defers: any, value: any): any; |
@@ -126,2 +126,10 @@ "use strict"; | ||
; | ||
function float32(bytes, value) { | ||
writeFloat32(bytes, value); | ||
} | ||
exports.float32 = float32; | ||
function float64(bytes, value) { | ||
writeFloat64(bytes, value); | ||
} | ||
exports.float64 = float64; | ||
var _isLittleEndian = new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1; | ||
@@ -133,3 +141,3 @@ var _int32 = new Int32Array(2); | ||
_float32[0] = value; | ||
int32(bytes, int32[0]); | ||
int32(bytes, _int32[0]); | ||
} | ||
@@ -145,2 +153,7 @@ exports.writeFloat32 = writeFloat32; | ||
; | ||
function boolean(bytes, value) { | ||
return uint8(bytes, value ? 1 : 0); | ||
} | ||
exports.boolean = boolean; | ||
; | ||
function string(bytes, value) { | ||
@@ -180,4 +193,8 @@ var length = utf8Length(value); | ||
if (Math.floor(value) !== value || !isFinite(value)) { | ||
/** | ||
* TODO: | ||
* is it possible to differentiate between float32 / float64 here? | ||
*/ | ||
bytes.push(0xcb); | ||
// defers.push({ _float: value, _length: 8, _offset: bytes.length }); | ||
writeFloat64(bytes, value); | ||
return 9; | ||
@@ -245,211 +262,1 @@ } | ||
exports.number = number; | ||
/** | ||
* UNUSED. LEFT HERE JUST FOR REFERENCE. | ||
*/ | ||
function encode(bytes, defers, value) { | ||
var type = typeof value, i = 0, l = 0, hi = 0, lo = 0, length = 0, size = 0; | ||
if (type === 'string') { | ||
length = utf8Length(value); | ||
// fixstr | ||
if (length < 0x20) { | ||
bytes.push(length | 0xa0); | ||
size = 1; | ||
} | ||
// str 8 | ||
else if (length < 0x100) { | ||
bytes.push(0xd9, length); | ||
size = 2; | ||
} | ||
// str 16 | ||
else if (length < 0x10000) { | ||
bytes.push(0xda, length >> 8, length); | ||
size = 3; | ||
} | ||
// str 32 | ||
else if (length < 0x100000000) { | ||
bytes.push(0xdb, length >> 24, length >> 16, length >> 8, length); | ||
size = 5; | ||
} | ||
else { | ||
throw new Error('String too long'); | ||
} | ||
defers.push({ _str: value, _length: length, _offset: bytes.length }); | ||
return size + length; | ||
} | ||
if (type === 'number') { | ||
// TODO: encode to float 32? | ||
// float 64 | ||
if (Math.floor(value) !== value || !isFinite(value)) { | ||
bytes.push(0xcb); | ||
defers.push({ _float: value, _length: 8, _offset: bytes.length }); | ||
return 9; | ||
} | ||
if (value >= 0) { | ||
// positive fixnum | ||
if (value < 0x80) { | ||
bytes.push(value); | ||
return 1; | ||
} | ||
// uint 8 | ||
if (value < 0x100) { | ||
bytes.push(0xcc, value); | ||
return 2; | ||
} | ||
// uint 16 | ||
if (value < 0x10000) { | ||
bytes.push(0xcd, value >> 8, value); | ||
return 3; | ||
} | ||
// uint 32 | ||
if (value < 0x100000000) { | ||
bytes.push(0xce, value >> 24, value >> 16, value >> 8, value); | ||
return 5; | ||
} | ||
// uint 64 | ||
hi = (value / Math.pow(2, 32)) >> 0; | ||
lo = value >>> 0; | ||
bytes.push(0xcf, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo); | ||
return 9; | ||
} | ||
else { | ||
// negative fixnum | ||
if (value >= -0x20) { | ||
bytes.push(value); | ||
return 1; | ||
} | ||
// int 8 | ||
if (value >= -0x80) { | ||
bytes.push(0xd0, value); | ||
return 2; | ||
} | ||
// int 16 | ||
if (value >= -0x8000) { | ||
bytes.push(0xd1, value >> 8, value); | ||
return 3; | ||
} | ||
// int 32 | ||
if (value >= -0x80000000) { | ||
bytes.push(0xd2, value >> 24, value >> 16, value >> 8, value); | ||
return 5; | ||
} | ||
// int 64 | ||
hi = Math.floor(value / Math.pow(2, 32)); | ||
lo = value >>> 0; | ||
bytes.push(0xd3, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo); | ||
return 9; | ||
} | ||
} | ||
if (type === 'object') { | ||
// nil | ||
if (value === null) { | ||
bytes.push(0xc0); | ||
return 1; | ||
} | ||
if (Array.isArray(value)) { | ||
length = value.length; | ||
// fixarray | ||
if (length < 0x10) { | ||
bytes.push(length | 0x90); | ||
size = 1; | ||
} | ||
// array 16 | ||
else if (length < 0x10000) { | ||
bytes.push(0xdc, length >> 8, length); | ||
size = 3; | ||
} | ||
// array 32 | ||
else if (length < 0x100000000) { | ||
bytes.push(0xdd, length >> 24, length >> 16, length >> 8, length); | ||
size = 5; | ||
} | ||
else { | ||
throw new Error('Array too large'); | ||
} | ||
for (i = 0; i < length; i++) { | ||
size += encode(bytes, defers, value[i]); | ||
} | ||
return size; | ||
} | ||
// fixext 8 / Date | ||
if (value instanceof Date) { | ||
var time = value.getTime(); | ||
hi = Math.floor(time / Math.pow(2, 32)); | ||
lo = time >>> 0; | ||
bytes.push(0xd7, 0, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo); | ||
return 10; | ||
} | ||
if (value instanceof ArrayBuffer) { | ||
length = value.byteLength; | ||
// bin 8 | ||
if (length < 0x100) { | ||
bytes.push(0xc4, length); | ||
size = 2; | ||
} | ||
else | ||
// bin 16 | ||
if (length < 0x10000) { | ||
bytes.push(0xc5, length >> 8, length); | ||
size = 3; | ||
} | ||
else | ||
// bin 32 | ||
if (length < 0x100000000) { | ||
bytes.push(0xc6, length >> 24, length >> 16, length >> 8, length); | ||
size = 5; | ||
} | ||
else { | ||
throw new Error('Buffer too large'); | ||
} | ||
defers.push({ _bin: value, _length: length, _offset: bytes.length }); | ||
return size + length; | ||
} | ||
if (typeof value.toJSON === 'function') { | ||
return encode(bytes, defers, value.toJSON()); | ||
} | ||
var keys = [], key = ''; | ||
var allKeys = Object.keys(value); | ||
for (i = 0, l = allKeys.length; i < l; i++) { | ||
key = allKeys[i]; | ||
if (typeof value[key] !== 'function') { | ||
keys.push(key); | ||
} | ||
} | ||
length = keys.length; | ||
// fixmap | ||
if (length < 0x10) { | ||
bytes.push(length | 0x80); | ||
size = 1; | ||
} | ||
// map 16 | ||
else if (length < 0x10000) { | ||
bytes.push(0xde, length >> 8, length); | ||
size = 3; | ||
} | ||
// map 32 | ||
else if (length < 0x100000000) { | ||
bytes.push(0xdf, length >> 24, length >> 16, length >> 8, length); | ||
size = 5; | ||
} | ||
else { | ||
throw new Error('Object too large'); | ||
} | ||
for (i = 0; i < length; i++) { | ||
key = keys[i]; | ||
size += encode(bytes, defers, key); | ||
size += encode(bytes, defers, value[key]); | ||
} | ||
return size; | ||
} | ||
// false/true | ||
if (type === 'boolean') { | ||
bytes.push(value ? 0xc3 : 0xc2); | ||
return 1; | ||
} | ||
// fixext 1 / undefined | ||
if (type === 'undefined') { | ||
bytes.push(0xd4, 0, 0); | ||
return 3; | ||
} | ||
throw new Error('Could not encode'); | ||
} | ||
exports.encode = encode; |
{ | ||
"name": "@colyseus/schema", | ||
"version": "0.1.8", | ||
"version": "0.3.0", | ||
"description": "", | ||
@@ -35,3 +35,3 @@ "main": "lib/index.js", | ||
"mocha": "^5.2.0", | ||
"nanoid": "^2.0.0", | ||
"nanoid": "^2.0.1", | ||
"notepack.io": "^2.2.0", | ||
@@ -38,0 +38,0 @@ "sinon": "^7.2.2", |
@@ -8,3 +8,3 @@ <div align="center"> | ||
A binary schema-based serialization algorithm. <br> | ||
Although it was born to solve a <a href="https://github.com/colyseus/colyseus">Colyseus</a> issue, this library can be used standalone. | ||
Although it was born to be used on <a href="https://github.com/colyseus/colyseus">Colyseus</a>, this library can be used as standalone. | ||
</p> | ||
@@ -38,3 +38,3 @@ | ||
```typescript | ||
import { DataChange, Schema, type } from '@colyseus/schema'; | ||
import { Schema, type, ArraySchema, MapSchema } from '@colyseus/schema'; | ||
@@ -63,10 +63,10 @@ export class Player extends Schema { | ||
@type([ Player ]) | ||
arrayOfPlayers: Player[]; | ||
arrayOfPlayers: ArraySchema<Player>; | ||
@type({ map: Player }) | ||
mapOfPlayers: { [id: string]: Player }; | ||
mapOfPlayers: MapSchema<Player>; | ||
} | ||
``` | ||
See [example/State.ts](example/State.ts). | ||
See [example](test/Schema.ts). | ||
@@ -81,2 +81,3 @@ ## Supported types | ||
| number | auto-detects `int` or `float` type. (extra byte on output) | `0` to `18446744073709551615` | | ||
| boolean | `true` or `false` | `0` or `1` | | ||
| int8 | signed 8-bit integer | `-128` to `127` | | ||
@@ -90,13 +91,62 @@ | uint8 | unsigned 8-bit integer | `0` to `255` | | ||
| uint64 | unsigned 64-bit integer | `0` to `18446744073709551615` | | ||
| float32 | single-precision floating-point number | `-3.40282347e+38` to `3.40282347e+38`| | ||
| float64 | double-precision floating-point number | `-1.7976931348623157e+308` to `1.7976931348623157e+308` | | ||
**Declaration:** | ||
### Declaration: | ||
- `@type("string") name: string;` | ||
- `@type("number") level: number;` | ||
- `@type(Player) player: Player;` | ||
- `@type([ Player ]) arrayOfPlayers: Player[];` | ||
- `@type([ "number" ]) arrayOfNumbers: number[];` | ||
- `@type([ "string" ]) arrayOfStrings: string[];` | ||
- `@type({ map: Player }) mapOfPlayers: {[id: string]: Player};` | ||
#### Primitive types (`string`, `number`, `boolean`, etc) | ||
```typescript | ||
@type("string") | ||
name: string; | ||
@type("int32") | ||
name: number; | ||
``` | ||
#### Custom `Schema` type | ||
```typescript | ||
@type(Player) | ||
player: Player; | ||
``` | ||
#### Array of a primitive type | ||
You can't mix types inside arrays. | ||
```typescript | ||
@type([ "number" ]) | ||
arrayOfNumbers: ArraySchema<number>; | ||
@type([ "string" ]) | ||
arrayOfStrings: ArraySchema<string>; | ||
``` | ||
#### Array of custom `Schema` type | ||
```typescript | ||
@type([ Player ]) | ||
arrayOfPlayers: ArraySchema<Player>; | ||
``` | ||
#### Map of a primitive type | ||
You can't mix types inside maps. | ||
```typescript | ||
@type({ map: "number" }) | ||
mapOfNumbers: MapSchema<number>; | ||
@type({ map: "string" }) | ||
mapOfStrings: MapSchema<string>; | ||
``` | ||
#### Map of custom `Schema` type | ||
```typescript | ||
@type({ map: Player }) | ||
mapOfPlayers: MapSchema<Player>; | ||
``` | ||
## Limitations and best practices | ||
@@ -103,0 +153,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
67336
23
1574
268