Comparing version 1.5.4 to 2.0.0-beta1
@@ -0,1 +1,16 @@ | ||
# 2.0.0 | ||
* #117 TypeScript conversion. Type definitions should now match behavior exactly. By @NaridaL | ||
* **BREAKING CHANGE**: `@serializeAll("*": ModelSchema)` is no longer valid. Instead you should pass | ||
`@serializeAll("*": object(ModelSchema))`. | ||
* **BREAKING CHANGE**: `{"*": object(...)}` and `{"x": object(...)}` now behave the same when | ||
deserializing `{"x": "str_not_object"}` (result: `{x: null}`). Previously the `"*"` schema would | ||
have returned `{}`. | ||
* **BREAKING CHANGE**: Removed deprecated `ref` and `child` functions. Use `reference` and `object` | ||
instead. | ||
* BREAKING CHANGE: Removed undocumented and untested code which possibly retried deserialization on | ||
error. | ||
* You can pass a `pattern` argument as `AdditionalPropArgs` instead of having to manually assign it | ||
to a PropSchema: `@serializeAll("*": list(primitive(), { pattern: /^_.*/ }))`. Note this only make | ||
sense together with the `"*"` property. | ||
# 1.5.4 | ||
@@ -10,3 +25,3 @@ * #113: Fix interpolation problem with IE11 by @danfma | ||
# 1.5.2 | ||
* Fixed potential memory leak, fixes #95 through #100 by @svennergr | ||
* Fixed potential memory leak, fixes #95 through #100 by @svennergr | ||
@@ -13,0 +28,0 @@ # 1.5.1 |
@@ -6,20 +6,20 @@ /** | ||
* @example | ||
* var todoSchema = createSimpleSchema({ | ||
* const todoSchema = createSimpleSchema({ | ||
* title: true, | ||
* done: true, | ||
* }); | ||
* }) | ||
* | ||
* var json = serialize(todoSchema, { title: 'Test', done: false }); | ||
* var todo = deserialize(todoSchema, json); | ||
* const json = serialize(todoSchema, { title: 'Test', done: false }) | ||
* const todo = deserialize(todoSchema, json) | ||
* | ||
* @param {object} props property mapping, | ||
* @returns {object} model schema | ||
* @param props property mapping, | ||
* @returns model schema | ||
*/ | ||
function createSimpleSchema(props) { | ||
return { | ||
factory: function() { | ||
return {} | ||
factory: function () { | ||
return {}; | ||
}, | ||
props: props | ||
} | ||
}; | ||
} | ||
@@ -30,37 +30,36 @@ | ||
try { | ||
return JSON.stringify(v) | ||
} catch (error) { | ||
return "[UnexpectedJSONParseError]: " + error.message | ||
return JSON.stringify(v); | ||
} | ||
catch (error) { | ||
return "[UnexpectedJSONParseError]: " + error.message; | ||
} | ||
}, | ||
l: function symbol(s) { | ||
return s.toString(); | ||
} | ||
}; | ||
function invariant(condition, message) { | ||
var variables = []; | ||
for (var _i = 2; _i < arguments.length; _i++) { | ||
variables[_i - 2] = arguments[_i]; | ||
} | ||
if (!condition) { | ||
var variables = Array.prototype.slice.call(arguments, 2); | ||
var variablesToLog = []; | ||
var index = 0; | ||
var formattedMessage = message.replace(/%([a-zA-Z%])/g, function messageFormatter(match, format) { | ||
if (match === "%%") return match | ||
var variablesToLog_1 = []; | ||
var index_1 = 0; | ||
var formattedMessage = message.replace(/%([a-zA-Z%])/g, function (match, format) { | ||
if (match === "%%") | ||
return match; | ||
var formatter = formatters[format]; | ||
if (typeof formatter === "function") { | ||
var variable = variables[index++]; | ||
variablesToLog.push(variable); | ||
return formatter(variable) | ||
var variable = variables[index_1++]; | ||
variablesToLog_1.push(variable); | ||
return formatter(variable); | ||
} | ||
return match | ||
return match; | ||
}); | ||
if (console && variablesToLog.length > 0) { | ||
if (console && variablesToLog_1.length > 0) { | ||
// eslint-disable-next-line no-console | ||
console.log.apply(console, variablesToLog); | ||
console.log.apply(console, variablesToLog_1); | ||
} | ||
throw new Error("[serializr] " + (formattedMessage || "Illegal State")) | ||
throw new Error("[serializr] " + (formattedMessage || "Illegal State")); | ||
} | ||
@@ -70,6 +69,6 @@ } | ||
function GUARDED_NOOP(err) { | ||
if (err) // unguarded error... | ||
throw new Error(err) | ||
if (err) | ||
// unguarded error... | ||
throw new Error(err); | ||
} | ||
function once(fn) { | ||
@@ -80,90 +79,77 @@ var fired = false; | ||
fired = true; | ||
return fn.apply(null, arguments) | ||
return fn.apply(null, arguments); | ||
} | ||
invariant(false, "callback was invoked twice"); | ||
} | ||
}; | ||
} | ||
function parallel(ar, processor, cb) { | ||
// TODO: limit parallelization? | ||
if (ar.length === 0) | ||
return void cb(null, []) | ||
var left = ar.filter(function(){ return true }).length; // only count items processed by forEach | ||
return void cb(null, []); | ||
var left = ar.filter(function (x) { return true; }).length; // only count items processed by forEach | ||
var resultArray = []; | ||
var failed = false; | ||
var processorCb = function (idx, err, result) { | ||
if (err) { | ||
if (!failed) { | ||
failed = true; | ||
cb(err); | ||
ar.forEach(function (value, idx) { | ||
processor(value, function (err, result) { | ||
if (err) { | ||
if (!failed) { | ||
failed = true; | ||
cb(err); | ||
} | ||
} | ||
} else { | ||
resultArray[idx] = result; | ||
if (--left === 0) | ||
cb(null, resultArray); | ||
} | ||
}; | ||
ar.forEach(function (value, idx) { | ||
processor(value, processorCb.bind(null, idx), idx); | ||
else { | ||
resultArray[idx] = result; | ||
if (--left === 0) | ||
cb(null, resultArray); | ||
} | ||
}, idx); | ||
}); | ||
} | ||
function isPrimitive(value) { | ||
if (value === null) | ||
return true | ||
return typeof value !== "object" && typeof value !== "function" | ||
return true; | ||
return typeof value !== "object" && typeof value !== "function"; | ||
} | ||
function isModelSchema(thing) { | ||
return thing && thing.factory && thing.props | ||
return thing && thing.factory && thing.props; | ||
} | ||
function isPropSchema(thing) { | ||
return thing && thing.serializer && thing.deserializer | ||
return thing && thing.serializer && thing.deserializer; | ||
} | ||
function isAliasedPropSchema(propSchema) { | ||
return typeof propSchema === "object" && !!propSchema.jsonname | ||
return typeof propSchema === "object" && "string" == typeof propSchema.jsonname; | ||
} | ||
function isIdentifierPropSchema(propSchema) { | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
return typeof propSchema === "object" && propSchema.identifier === true; | ||
} | ||
function isAssignableTo(actualType, expectedType) { | ||
while (actualType) { | ||
if (actualType === expectedType) | ||
return true | ||
actualType = actualType.extends; | ||
var currentActualType = actualType; | ||
while (currentActualType) { | ||
if (currentActualType === expectedType) | ||
return true; | ||
currentActualType = currentActualType.extends; | ||
} | ||
return false | ||
return false; | ||
} | ||
function isMapLike(thing) { | ||
return thing && typeof thing.keys === "function" && typeof thing.clear === "function" | ||
return thing && typeof thing.keys === "function" && typeof thing.clear === "function"; | ||
} | ||
function getIdentifierProp(modelSchema) { | ||
invariant(isModelSchema(modelSchema)); | ||
invariant(isModelSchema(modelSchema), "modelSchema must be a ModelSchema"); | ||
// optimization: cache this lookup | ||
while (modelSchema) { | ||
for (var propName in modelSchema.props) | ||
if (typeof modelSchema.props[propName] === "object" && modelSchema.props[propName].identifier === true) | ||
return propName | ||
modelSchema = modelSchema.extends; | ||
var currentModelSchema = modelSchema; | ||
while (currentModelSchema) { | ||
for (var propName in currentModelSchema.props) | ||
if (isIdentifierPropSchema(currentModelSchema.props[propName])) | ||
return propName; | ||
currentModelSchema = currentModelSchema.extends; | ||
} | ||
return null | ||
return undefined; | ||
} | ||
function processAdditionalPropArgs(propSchema, additionalArgs) { | ||
if (additionalArgs) { | ||
invariant(isPropSchema(propSchema), "expected a propSchema"); | ||
var argNames = ["beforeDeserialize", "afterDeserialize"]; | ||
argNames.forEach(function(argName) { | ||
if (typeof additionalArgs[argName] === "function") { | ||
propSchema[argName] = additionalArgs[argName]; | ||
} | ||
}); | ||
Object.assign(propSchema, additionalArgs); | ||
} | ||
return propSchema | ||
return propSchema; | ||
} | ||
@@ -174,14 +160,12 @@ | ||
* | ||
* @param {object} thing | ||
* @returns {ModelSchema} model schema | ||
*/ | ||
function getDefaultModelSchema(thing) { | ||
if (!thing) | ||
return null | ||
return undefined; | ||
if (isModelSchema(thing)) | ||
return thing | ||
return thing; | ||
if (isModelSchema(thing.serializeInfo)) | ||
return thing.serializeInfo | ||
return thing.serializeInfo; | ||
if (thing.constructor && thing.constructor.serializeInfo) | ||
return thing.constructor.serializeInfo | ||
return thing.constructor.serializeInfo; | ||
} | ||
@@ -197,9 +181,10 @@ | ||
* | ||
* @param {constructor|class} clazz class or constructor function | ||
* @param {ModelSchema} modelSchema - a model schema | ||
* @returns {ModelSchema} model schema | ||
* @param clazz class or constructor function | ||
* @param modelSchema - a model schema | ||
* @returns model schema | ||
*/ | ||
function setDefaultModelSchema(clazz, modelSchema) { | ||
invariant(isModelSchema(modelSchema)); | ||
return clazz.serializeInfo = modelSchema | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema); | ||
clazz.serializeInfo = modelSchema; | ||
return modelSchema; | ||
} | ||
@@ -214,4 +199,4 @@ | ||
* function Todo(title, done) { | ||
* this.title = title; | ||
* this.done = done; | ||
* this.title = title | ||
* this.done = done | ||
* } | ||
@@ -222,11 +207,11 @@ * | ||
* done: true, | ||
* }); | ||
* }) | ||
* | ||
* var json = serialize(new Todo('Test', false)); | ||
* var todo = deserialize(Todo, json); | ||
* const json = serialize(new Todo('Test', false)) | ||
* const todo = deserialize(Todo, json) | ||
* | ||
* @param {constructor|class} clazz class or constructor function | ||
* @param {object} props property mapping | ||
* @param {function} factory optional custom factory. Receives context as first arg | ||
* @returns {object} model schema | ||
* @param clazz class or constructor function | ||
* @param props property mapping | ||
* @param factory optional custom factory. Receives context as first arg | ||
* @returns model schema | ||
*/ | ||
@@ -238,5 +223,6 @@ function createModelSchema(clazz, props, factory) { | ||
targetClass: clazz, | ||
factory: factory || function() { | ||
return new clazz() | ||
}, | ||
factory: factory || | ||
function () { | ||
return new clazz(); | ||
}, | ||
props: props | ||
@@ -251,5 +237,28 @@ }; | ||
setDefaultModelSchema(clazz, model); | ||
return model | ||
return model; | ||
} | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
function __spreadArrays() { | ||
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; | ||
for (var r = Array(s), k = 0, i = 0; i < il; i++) | ||
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) | ||
r[k] = a[j]; | ||
return r; | ||
} | ||
/** | ||
@@ -261,9 +270,7 @@ * Indicates that this field contains a primitive value (or Date) which should be serialized literally to json. | ||
* title: primitive(), | ||
* }); | ||
* }) | ||
* | ||
* console.dir(serialize(new Todo('test'))); | ||
* // outputs: { title : "test" } | ||
* serialize(new Todo('test')) // { "title": "test" } | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
* @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
*/ | ||
@@ -274,47 +281,58 @@ function primitive(additionalArgs) { | ||
invariant(isPrimitive(value), "this value is not primitive: " + value); | ||
return value | ||
return value; | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
if (!isPrimitive(jsonValue)) | ||
return void done("[serializr] this value is not primitive: " + jsonValue) | ||
return void done(null, jsonValue) | ||
return void done("[serializr] this value is not primitive: " + jsonValue); | ||
return void done(null, jsonValue); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
/** | ||
* In the event that a property needs to be deserialized, but not serialized, you can use the SKIP symbol to omit the property. This has to be used with the custom serializer. | ||
* If you want to skip serialization or deserialization, you can use SKIP. | ||
* | ||
* @example | ||
* var schema = _.createSimpleSchema({ | ||
* a: _.custom( | ||
* function(v) { | ||
* return _.SKIP | ||
* }, | ||
* function(v) { | ||
* return v; | ||
* } | ||
* const schema = createSimpleSchema({ | ||
* a: custom( | ||
* () => SKIP, | ||
* v => v, | ||
* ), | ||
* }); | ||
* t.deepEqual(_.serialize(s, { a: 4 }), { }); | ||
* t.deepEqual(_.deserialize(s, { a: 4 }), { a: 4 }); | ||
* }) | ||
* serialize(s, { a: 4 }) // {} | ||
* deserialize(s, { "a": 4 }) // { a: 4 } | ||
* | ||
* @example | ||
* // Skipping deserialization with computed mobx property. | ||
* | ||
* class TodoState { | ||
* // Todo.category is @serializable(reference(...)) | ||
* \@serializable(list(object(Todo))) | ||
* \@observable | ||
* todos: Todo[] | ||
* | ||
* // we want to serialize the categories, so that the references in | ||
* // this.todos can be resolved, but we don't want to set this property | ||
* \@serializable( | ||
* list(object(TodoCategory), | ||
* { afterDeserialize: callback => callback(undefined, SKIP) })) | ||
* \@computed | ||
* get categories() { | ||
* return this.todos.map(todo => todo.category) | ||
* } | ||
* } | ||
*/ | ||
var SKIP = typeof Symbol !== "undefined" ? Symbol("SKIP") : { SKIP: true }; | ||
var _defaultPrimitiveProp = primitive(); | ||
// Ugly way to get the parameter names since they aren't easily retrievable via reflection | ||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; | ||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; | ||
var ARGUMENT_NAMES = /([^\s,]+)/g; | ||
function getParamNames(func) { | ||
var _a; | ||
var fnStr = func.toString().replace(STRIP_COMMENTS, ""); | ||
var result = fnStr.slice(fnStr.indexOf("(")+1, fnStr.indexOf(")")).match(ARGUMENT_NAMES); | ||
if(result === null) | ||
result = []; | ||
return result | ||
return _a = fnStr.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")")).match(ARGUMENT_NAMES), (_a !== null && _a !== void 0 ? _a : []); | ||
} | ||
function serializableDecorator(propSchema, target, propName, descriptor) { | ||
@@ -324,7 +342,9 @@ invariant(arguments.length >= 2, "too few arguments. Please use @serializable as property decorator"); | ||
var factory; | ||
if (propName === undefined && typeof target === "function" | ||
&& target.prototype | ||
&& descriptor !== undefined && typeof descriptor === "number") { | ||
if (propName === undefined && | ||
typeof target === "function" && | ||
target.prototype && | ||
descriptor !== undefined && | ||
typeof descriptor === "number") { | ||
invariant(isPropSchema(propSchema), "Constructor params must use alias(name)"); | ||
invariant(propSchema.jsonname, "Constructor params must use alias(name)"); | ||
invariant(isAliasedPropSchema(propSchema), "Constructor params must use alias(name)"); | ||
var paramNames = getParamNames(target); | ||
@@ -337,5 +357,6 @@ if (paramNames.length >= descriptor) { | ||
// Create a factory so the constructor is called properly | ||
factory = function(context) { | ||
factory = function (context) { | ||
var _a; | ||
var params = []; | ||
for (var i = 0; i < target.constructor.length; i++) { | ||
var _loop_1 = function (i) { | ||
Object.keys(context.modelSchema.props).forEach(function (key) { | ||
@@ -347,5 +368,7 @@ var prop = context.modelSchema.props[key]; | ||
}); | ||
}; | ||
for (var i = 0; i < target.constructor.length; i++) { | ||
_loop_1(i); | ||
} | ||
return new (Function.prototype.bind.apply(target.constructor, [null].concat(params))) | ||
return (_a = target.constructor).bind.apply(_a, __spreadArrays([undefined], params)); | ||
}; | ||
@@ -356,3 +379,2 @@ } | ||
var info = getDefaultModelSchema(target); | ||
if (!info || !target.constructor.hasOwnProperty("serializeInfo")) | ||
@@ -367,82 +389,43 @@ info = createModelSchema(target.constructor, {}, factory); | ||
descriptor.writable = true; | ||
return descriptor | ||
return descriptor; | ||
} | ||
/** | ||
* Decorator that defines a new property mapping on the default model schema for the class | ||
* it is used in. | ||
* | ||
* When using typescript, the decorator can also be used on fields declared as constructor arguments (using the `private` / `protected` / `public` keywords). | ||
* The default factory will then invoke the constructor with the correct arguments as well. | ||
* | ||
* @example | ||
* class Todo { | ||
* @serializable(primitive()) | ||
* title; // shorthand for primitves | ||
* | ||
* @serializable done; | ||
* | ||
* constructor(title, done) { | ||
* this.title = title; | ||
* this.done = done; | ||
* } | ||
* } | ||
* | ||
* var json = serialize(new Todo('Test', false)); | ||
* var todo = deserialize(Todo, json); | ||
* | ||
* @param arg1 | ||
* @param arg2 | ||
* @param arg3 | ||
* @returns {PropertyDescriptor} | ||
*/ | ||
function serializable(arg1, arg2, arg3) { | ||
if (arguments.length === 1) { | ||
function serializable(targetOrPropSchema, key, baseDescriptor) { | ||
if (!key) { | ||
// decorated with propSchema | ||
var propSchema = arg1 === true ? _defaultPrimitiveProp : arg1; | ||
var propSchema = targetOrPropSchema === true ? _defaultPrimitiveProp : targetOrPropSchema; | ||
invariant(isPropSchema(propSchema), "@serializable expects prop schema"); | ||
return serializableDecorator.bind(null, propSchema) | ||
} else { | ||
var result = serializableDecorator.bind(null, propSchema); | ||
return result; | ||
} | ||
else { | ||
// decorated without arguments, treat as primitive | ||
return serializableDecorator(primitive(), arg1, arg2, arg3) | ||
serializableDecorator(primitive(), targetOrPropSchema, key, baseDescriptor); | ||
} | ||
} | ||
/** | ||
* Serializes an object (graph) into json using the provided model schema. | ||
* The model schema can be omitted if the object type has a default model schema associated with it. | ||
* If a list of objects is provided, they should have an uniform type. | ||
* | ||
* @param arg1 class or modelschema to use. Optional | ||
* @param arg2 object(s) to serialize | ||
* @returns {object} serialized representation of the object | ||
*/ | ||
function serialize(arg1, arg2) { | ||
function serialize(modelSchemaOrInstance, arg2) { | ||
invariant(arguments.length === 1 || arguments.length === 2, "serialize expects one or 2 arguments"); | ||
var thing = arguments.length === 1 ? arg1 : arg2; | ||
var schema = arguments.length === 1 ? null : arg1; | ||
if (Array.isArray(thing)) { | ||
if (thing.length === 0) | ||
return [] // don't bother finding a schema | ||
var instance = ((arg2 !== null && arg2 !== void 0 ? arg2 : modelSchemaOrInstance)); | ||
var schema = (arg2 && modelSchemaOrInstance); | ||
if (Array.isArray(instance)) { | ||
if (instance.length === 0) | ||
return []; | ||
// don't bother finding a schema | ||
else if (!schema) | ||
schema = getDefaultModelSchema(thing[0]); | ||
schema = getDefaultModelSchema(instance[0]); | ||
else if (typeof schema !== "object") | ||
schema = getDefaultModelSchema(schema); | ||
} else if (!schema) { | ||
schema = getDefaultModelSchema(thing); | ||
} else if (typeof schema !== "object") { | ||
} | ||
else if (!schema) { | ||
schema = getDefaultModelSchema(instance); | ||
} | ||
else if (typeof schema !== "object") { | ||
schema = getDefaultModelSchema(schema); | ||
} | ||
invariant(!!schema, "Failed to find default schema for " + arg1); | ||
if (Array.isArray(thing)) | ||
return thing.map(function (item) { | ||
return serializeWithSchema(schema, item) | ||
}) | ||
return serializeWithSchema(schema, thing) | ||
var foundSchema = schema; | ||
invariant(foundSchema, "Failed to find default schema for " + modelSchemaOrInstance); | ||
if (Array.isArray(instance)) | ||
return instance.map(function (item) { return serializeWithSchema(foundSchema, item); }); | ||
return serializeWithSchema(foundSchema, instance); | ||
} | ||
function checkStarSchemaInvariant(propDef) { | ||
invariant(propDef === true || propDef.pattern, "prop schema '*' can only be used with 'true' or a prop def with a 'pattern': " + JSON.stringify(propDef)); | ||
} | ||
function serializeWithSchema(schema, obj) { | ||
@@ -460,42 +443,37 @@ invariant(schema && typeof schema === "object" && schema.props, "Expected schema"); | ||
var propDef = schema.props[key]; | ||
if (!propDef) | ||
return; | ||
if (key === "*") { | ||
serializeStarProps(schema, propDef, obj, res); | ||
return | ||
return; | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp; | ||
if (propDef === false) | ||
return | ||
var jsonValue = propDef.serializer(obj[key], key, obj); | ||
if (jsonValue === SKIP){ | ||
return | ||
if (jsonValue === SKIP) { | ||
return; | ||
} | ||
res[propDef.jsonname || key] = jsonValue; | ||
}); | ||
return res | ||
return res; | ||
} | ||
function serializeStarProps(schema, propDef, obj, target) { | ||
checkStarSchemaInvariant(propDef); | ||
for (var key in obj) if (obj.hasOwnProperty(key)) if (!(key in schema.props)) { | ||
if ((propDef === true) || (propDef.pattern && propDef.pattern.test(key))) { | ||
var value = obj[key]; | ||
if (propDef === true) { | ||
if (isPrimitive(value)) { | ||
target[key] = value; | ||
for (var _i = 0, _a = Object.keys(obj); _i < _a.length; _i++) { | ||
var key = _a[_i]; | ||
if (!(key in schema.props)) { | ||
if (propDef === true || (propDef && (!propDef.pattern || propDef.pattern.test(key)))) { | ||
var value = obj[key]; | ||
if (propDef === true) { | ||
if (isPrimitive(value)) { | ||
target[key] = value; | ||
} | ||
} | ||
} else if (propDef.props) { | ||
var jsonValue = serialize(propDef, value); | ||
if (jsonValue === SKIP){ | ||
return | ||
else { | ||
var jsonValue = propDef.serializer(value, key, obj); | ||
if (jsonValue === SKIP) { | ||
return; | ||
} | ||
// TODO: propDef.jsonname could be a transform function on key | ||
target[key] = jsonValue; | ||
} | ||
// todo: propDef.jsonname could be a transform function on key | ||
target[key] = jsonValue; | ||
} else { | ||
var jsonValue = propDef.serializer(value, key, obj); | ||
if (jsonValue === SKIP){ | ||
return | ||
} | ||
// todo: propDef.jsonname could be a transform function on key | ||
target[key] = jsonValue; | ||
} | ||
@@ -507,122 +485,121 @@ } | ||
var rootContextCache = new WeakMap(); | ||
function Context(parentContext, modelSchema, json, onReadyCb, customArgs) { | ||
this.parentContext = parentContext; | ||
this.isRoot = !parentContext; | ||
this.pendingCallbacks = 0; | ||
this.pendingRefsCount = 0; | ||
this.onReadyCb = onReadyCb || GUARDED_NOOP; | ||
this.json = json; | ||
this.target = null; // always set this property using setTarget | ||
this.hasError = false; | ||
this.modelSchema = modelSchema; | ||
if (this.isRoot) { | ||
this.rootContext = this; | ||
this.args = customArgs; | ||
this.pendingRefs = {}; // uuid: [{ modelSchema, uuid, cb }] | ||
this.resolvedRefs = {}; // uuid: [{ modelSchema, value }] | ||
} else { | ||
this.rootContext = parentContext.rootContext; | ||
this.args = parentContext.args; | ||
var Context = /** @class */ (function () { | ||
function Context(parentContext, modelSchema, json, onReadyCb, customArgs) { | ||
this.parentContext = parentContext; | ||
this.modelSchema = modelSchema; | ||
this.json = json; | ||
this.onReadyCb = onReadyCb; | ||
this.isRoot = !parentContext; | ||
this.pendingCallbacks = 0; | ||
this.pendingRefsCount = 0; | ||
this.target = undefined; // always set this property using setTarget | ||
this.hasError = false; | ||
if (!parentContext) { | ||
this.rootContext = this; | ||
this.args = customArgs; | ||
this.pendingRefs = {}; | ||
this.resolvedRefs = {}; | ||
} | ||
else { | ||
this.rootContext = parentContext.rootContext; | ||
this.args = parentContext.args; | ||
} | ||
} | ||
} | ||
Context.prototype.createCallback = function (fn) { | ||
this.pendingCallbacks++; | ||
// once: defend against user-land calling 'done' twice | ||
return once(function (err, value) { | ||
if (err) { | ||
if (!this.hasError) { | ||
this.hasError = true; | ||
this.onReadyCb(err); | ||
rootContextCache.delete(this); | ||
Context.prototype.createCallback = function (fn) { | ||
var _this = this; | ||
this.pendingCallbacks++; | ||
// once: defend against user-land calling 'done' twice | ||
return once(function (err, value) { | ||
if (err) { | ||
if (!_this.hasError) { | ||
_this.hasError = true; | ||
_this.onReadyCb(err); | ||
rootContextCache.delete(_this); | ||
} | ||
} | ||
} else if (!this.hasError) { | ||
fn(value); | ||
if (--this.pendingCallbacks === this.pendingRefsCount) { | ||
if (this.pendingRefsCount > 0) { | ||
// all pending callbacks are pending reference resolvers. not good. | ||
this.onReadyCb(new Error( | ||
"Unresolvable references in json: \"" + | ||
Object.keys(this.pendingRefs).filter(function (uuid) { | ||
return this.pendingRefs[uuid].length > 0 | ||
}, this).join("\", \"") + | ||
"\"" | ||
)); | ||
rootContextCache.delete(this); | ||
} else { | ||
this.onReadyCb(null, this.target); | ||
rootContextCache.delete(this); | ||
else if (!_this.hasError) { | ||
fn(value); | ||
if (--_this.pendingCallbacks === _this.pendingRefsCount) { | ||
if (_this.pendingRefsCount > 0) { | ||
// all pending callbacks are pending reference resolvers. not good. | ||
_this.onReadyCb(new Error('Unresolvable references in json: "' + | ||
Object.keys(_this.pendingRefs) | ||
.filter(function (uuid) { return _this.pendingRefs[uuid].length > 0; }) | ||
.join('", "') + | ||
'"')); | ||
rootContextCache.delete(_this); | ||
} | ||
else { | ||
_this.onReadyCb(null, _this.target); | ||
rootContextCache.delete(_this); | ||
} | ||
} | ||
} | ||
}); | ||
}; | ||
// given an object with uuid, modelSchema, callback, awaits until the given uuid is available | ||
// resolve immediately if possible | ||
Context.prototype.await = function (modelSchema, uuid, callback) { | ||
invariant(this.isRoot, "await can only be called on the root context"); | ||
if (uuid in this.resolvedRefs) { | ||
var match = this.resolvedRefs[uuid].filter(function (resolved) { | ||
return isAssignableTo(resolved.modelSchema, modelSchema); | ||
})[0]; | ||
if (match) | ||
return void callback(null, match.value); | ||
} | ||
}.bind(this)) | ||
}; | ||
// given an object with uuid, modelSchema, callback, awaits until the given uuid is available | ||
// resolve immediately if possible | ||
Context.prototype.await = function (modelSchema, uuid, callback) { | ||
invariant(this.isRoot); | ||
if (uuid in this.resolvedRefs) { | ||
var match = this.resolvedRefs[uuid].filter(function (resolved) { | ||
return isAssignableTo(resolved.modelSchema, modelSchema) | ||
})[0]; | ||
if (match) | ||
return void callback(null, match.value) | ||
} | ||
this.pendingRefsCount++; | ||
if (!this.pendingRefs[uuid]) | ||
this.pendingRefs[uuid] = []; | ||
this.pendingRefs[uuid].push({ | ||
modelSchema: modelSchema, | ||
uuid: uuid, | ||
callback: callback | ||
}); | ||
}; | ||
// given a model schema, uuid and value, resolve all references that where looking for this object | ||
Context.prototype.resolve = function (modelSchema, uuid, value) { | ||
invariant(this.isRoot); | ||
if (!this.resolvedRefs[uuid]) | ||
this.resolvedRefs[uuid] = []; | ||
this.resolvedRefs[uuid].push({ | ||
modelSchema: modelSchema, value: value | ||
}); | ||
if (uuid in this.pendingRefs) { | ||
for (var i = this.pendingRefs[uuid].length - 1; i >= 0; i--) { | ||
var opts = this.pendingRefs[uuid][i]; | ||
if (isAssignableTo(modelSchema, opts.modelSchema)) { | ||
this.pendingRefs[uuid].splice(i, 1); | ||
this.pendingRefsCount--; | ||
opts.callback(null, value); | ||
this.pendingRefsCount++; | ||
if (!this.pendingRefs[uuid]) | ||
this.pendingRefs[uuid] = []; | ||
this.pendingRefs[uuid].push({ | ||
modelSchema: modelSchema, | ||
uuid: uuid, | ||
callback: callback | ||
}); | ||
}; | ||
// given a model schema, uuid and value, resolve all references that were looking for this object | ||
Context.prototype.resolve = function (modelSchema, uuid, value) { | ||
invariant(this.isRoot, "resolve can only called on the root context"); | ||
if (!this.resolvedRefs[uuid]) | ||
this.resolvedRefs[uuid] = []; | ||
this.resolvedRefs[uuid].push({ | ||
modelSchema: modelSchema, | ||
value: value | ||
}); | ||
if (uuid in this.pendingRefs) { | ||
for (var i = this.pendingRefs[uuid].length - 1; i >= 0; i--) { | ||
var opts = this.pendingRefs[uuid][i]; | ||
if (isAssignableTo(modelSchema, opts.modelSchema)) { | ||
this.pendingRefs[uuid].splice(i, 1); | ||
this.pendingRefsCount--; | ||
opts.callback(null, value); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
// set target and update root context cache | ||
Context.prototype.setTarget = function (target) { | ||
if (this.isRoot && this.target) { | ||
rootContextCache.delete(this.target); | ||
} | ||
this.target = target; | ||
rootContextCache.set(this.target, this); | ||
}; | ||
// call all remaining reference lookup callbacks indicating an error during ref resolution | ||
Context.prototype.cancelAwaits = function () { | ||
invariant(this.isRoot); | ||
var self = this; | ||
Object.keys(this.pendingRefs).forEach(function (uuid) { | ||
self.pendingRefs[uuid].forEach(function (refOpts) { | ||
self.pendingRefsCount--; | ||
refOpts.callback(new Error("Reference resolution canceled for " + uuid)); | ||
}; | ||
// set target and update root context cache | ||
Context.prototype.setTarget = function (target) { | ||
if (this.isRoot && this.target) { | ||
rootContextCache.delete(this.target); | ||
} | ||
this.target = target; | ||
rootContextCache.set(this.target, this); | ||
}; | ||
// call all remaining reference lookup callbacks indicating an error during ref resolution | ||
Context.prototype.cancelAwaits = function () { | ||
invariant(this.isRoot, "cancelAwaits can only be called on the root context"); | ||
var self = this; | ||
Object.keys(this.pendingRefs).forEach(function (uuid) { | ||
self.pendingRefs[uuid].forEach(function (refOpts) { | ||
self.pendingRefsCount--; | ||
refOpts.callback(new Error("Reference resolution canceled for " + uuid)); | ||
}); | ||
}); | ||
}); | ||
this.pendingRefs = {}; | ||
this.pendingRefsCount = 0; | ||
}; | ||
this.pendingRefs = {}; | ||
this.pendingRefsCount = 0; | ||
}; | ||
return Context; | ||
}()); | ||
function getTargetContext(target) { | ||
return rootContextCache.get(target) | ||
return rootContextCache.get(target); | ||
} | ||
@@ -633,85 +610,57 @@ | ||
*/ | ||
function schemaHasAlias(schema, name) { | ||
for (var key in schema.props) | ||
if (typeof schema.props[key] === "object" && schema.props[key].jsonname === name) | ||
return true | ||
return false | ||
for (var key in schema.props) { | ||
var propSchema = schema.props[key]; | ||
if (typeof propSchema === "object" && propSchema.jsonname === name) | ||
return true; | ||
} | ||
return false; | ||
} | ||
function deserializeStarProps(context, schema, propDef, obj, json) { | ||
checkStarSchemaInvariant(propDef); | ||
for (var key in json) if (!(key in schema.props) && !schemaHasAlias(schema, key)) { | ||
var jsonValue = json[key]; | ||
if (propDef === true) { | ||
// when deserializing we don't want to silently ignore 'unparseable data' to avoid | ||
// confusing bugs | ||
invariant(isPrimitive(jsonValue), | ||
"encountered non primitive value while deserializing '*' properties in property '" + | ||
key + "': " + jsonValue); | ||
obj[key] = jsonValue; | ||
} else if (propDef.pattern.test(key)) { | ||
if (propDef.factory) { | ||
var resultValue = deserializeObjectWithSchema(context, propDef, jsonValue, context.callback || GUARDED_NOOP, {}); | ||
// deserializeObjectWithSchema returns undefined on error | ||
if (resultValue !== undefined) { | ||
obj[key] = resultValue; | ||
} | ||
} else { | ||
function setValue(resultValue) { | ||
if (resultValue !== SKIP) { | ||
obj[key] = resultValue; | ||
} | ||
} | ||
propDef.deserializer(jsonValue, | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
context.rootContext.createCallback(setValue), | ||
context); | ||
var _loop_1 = function (key) { | ||
if (!(key in schema.props) && !schemaHasAlias(schema, key)) { | ||
var jsonValue = json[key]; | ||
if (propDef === true) { | ||
// when deserializing we don't want to silently ignore 'unparseable data' to avoid | ||
// confusing bugs | ||
invariant(isPrimitive(jsonValue), "encountered non primitive value while deserializing '*' properties in property '" + | ||
key + | ||
"': " + | ||
jsonValue); | ||
obj[key] = jsonValue; | ||
} | ||
else if (propDef && (!propDef.pattern || propDef.pattern.test(key))) { | ||
propDef.deserializer(jsonValue, | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
context.rootContext.createCallback(function (r) { return r !== SKIP && (obj[key] = r); }), context); | ||
} | ||
} | ||
}; | ||
for (var key in json) { | ||
_loop_1(key); | ||
} | ||
} | ||
/** | ||
* Deserializes a json structure into an object graph. | ||
* | ||
* This process might be asynchronous (for example if there are references with an asynchronous | ||
* lookup function). The function returns an object (or array of objects), but the returned object | ||
* might be incomplete until the callback has fired as well (which might happen immediately) | ||
* | ||
* @param {object|array} schema to use for deserialization | ||
* @param {json} json data to deserialize | ||
* @param {function} callback node style callback that is invoked once the deserialization has | ||
* finished. First argument is the optional error, second argument is the deserialized object | ||
* (same as the return value) | ||
* @param {*} customArgs custom arguments that are available as `context.args` during the | ||
* deserialization process. This can be used as dependency injection mechanism to pass in, for | ||
* example, stores. | ||
* @returns {object|array} deserialized object, possibly incomplete. | ||
*/ | ||
function deserialize(schema, json, callback, customArgs) { | ||
function deserialize(clazzOrModelSchema, json, callback, customArgs) { | ||
if (callback === void 0) { callback = GUARDED_NOOP; } | ||
invariant(arguments.length >= 2, "deserialize expects at least 2 arguments"); | ||
schema = getDefaultModelSchema(schema); | ||
var schema = getDefaultModelSchema(clazzOrModelSchema); | ||
invariant(isModelSchema(schema), "first argument should be model schema"); | ||
if (Array.isArray(json)) { | ||
var items = []; | ||
parallel( | ||
json, | ||
function (childJson, itemDone) { | ||
var instance = deserializeObjectWithSchema(null, schema, childJson, itemDone, customArgs); | ||
// instance is created synchronously so can be pushed | ||
items.push(instance); | ||
}, | ||
callback || GUARDED_NOOP | ||
); | ||
return items | ||
} else | ||
return deserializeObjectWithSchema(null, schema, json, callback, customArgs) | ||
var items_1 = []; | ||
parallel(json, function (childJson, itemDone) { | ||
var instance = deserializeObjectWithSchema(undefined, schema, childJson, itemDone, customArgs); | ||
// instance is created synchronously so can be pushed | ||
items_1.push(instance); | ||
}, callback); | ||
return items_1; | ||
} | ||
else { | ||
return deserializeObjectWithSchema(undefined, schema, json, callback, customArgs); | ||
} | ||
} | ||
function deserializeObjectWithSchema(parentContext, modelSchema, json, callback, customArgs) { | ||
if (json === null || json === undefined || typeof json !== "object") | ||
return void callback(null, null) | ||
return void callback(null, null); | ||
var context = new Context(parentContext, modelSchema, json, callback, customArgs); | ||
@@ -728,94 +677,63 @@ var target = modelSchema.factory(context); | ||
lock(); | ||
return target | ||
return target; | ||
} | ||
function deserializePropsWithSchema(context, modelSchema, json, target) { | ||
var _a; | ||
if (modelSchema.extends) | ||
deserializePropsWithSchema(context, modelSchema.extends, json, target); | ||
function deserializeProp(propDef, jsonValue, propName) { | ||
function setValue(value) { | ||
if (value !== SKIP) { | ||
target[propName] = value; | ||
} | ||
} | ||
function preProcess(resultCallback) { | ||
return function (err, newValue) { | ||
function finalCallback(errPreliminary, finalOrRetryValue) { | ||
if (errPreliminary && finalOrRetryValue !== undefined && | ||
typeof propDef.afterDeserialize === "function") { | ||
propDef.deserializer( | ||
finalOrRetryValue, | ||
preProcess(resultCallback), | ||
context, | ||
target[propName] | ||
); | ||
} else { | ||
resultCallback(errPreliminary, finalOrRetryValue); | ||
} | ||
} | ||
onAfterDeserialize(finalCallback, err, newValue, jsonValue, json, | ||
propName, context, propDef); | ||
} | ||
} | ||
propDef.deserializer( | ||
jsonValue, | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
preProcess(context.rootContext.createCallback(setValue)), | ||
context, | ||
target[propName] // initial value | ||
var whenDone = context.rootContext.createCallback(function (r) { return r !== SKIP && (target[propName] = r); }); | ||
propDef.deserializer(jsonValue, | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
function (err, newValue) { | ||
return onAfterDeserialize(whenDone, err, newValue, jsonValue, json, propName, context, propDef); | ||
}, context, target[propName] // initial value | ||
); | ||
} | ||
Object.keys(modelSchema.props).forEach(function (propName) { | ||
var propDef = modelSchema.props[propName]; | ||
function callbackDeserialize(err, jsonValue) { | ||
if (!err && jsonValue !== undefined) { | ||
deserializeProp(propDef, jsonValue, propName); | ||
} | ||
} | ||
if (propName === "*") { | ||
var _loop_2 = function (key) { | ||
var propDef = modelSchema.props[key]; | ||
if (!propDef) | ||
return { value: void 0 }; | ||
if (key === "*") { | ||
deserializeStarProps(context, modelSchema, propDef, target, json); | ||
return | ||
return { value: void 0 }; | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp; | ||
if (propDef === false) | ||
return | ||
var jsonAttr = propDef.jsonname || propName; | ||
var jsonAttr = (_a = propDef.jsonname, (_a !== null && _a !== void 0 ? _a : key)); | ||
invariant("symbol" !== typeof jsonAttr, "You must alias symbol properties. prop = %l", key); | ||
var jsonValue = json[jsonAttr]; | ||
var propSchema = propDef; | ||
var callbackDeserialize = function (err, jsonValue) { | ||
if (!err && jsonValue !== undefined) { | ||
deserializeProp(propSchema, jsonValue, key); | ||
} | ||
}; | ||
onBeforeDeserialize(callbackDeserialize, jsonValue, json, jsonAttr, context, propDef); | ||
}); | ||
}; | ||
for (var _i = 0, _b = Object.keys(modelSchema.props); _i < _b.length; _i++) { | ||
var key = _b[_i]; | ||
var state_1 = _loop_2(key); | ||
if (typeof state_1 === "object") | ||
return state_1.value; | ||
} | ||
} | ||
function onBeforeDeserialize( | ||
callback, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
var onBeforeDeserialize = function (callback, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
if (propDef && typeof propDef.beforeDeserialize === "function") { | ||
propDef.beforeDeserialize(callback, jsonValue, jsonParentValue, propNameOrIndex, context, | ||
propDef); | ||
} else { | ||
propDef.beforeDeserialize(callback, jsonValue, jsonParentValue, propNameOrIndex, context, propDef); | ||
} | ||
else { | ||
callback(null, jsonValue); | ||
} | ||
} | ||
function onAfterDeserialize( | ||
callback, err, newValue, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
}; | ||
var onAfterDeserialize = function (callback, err, newValue, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
if (propDef && typeof propDef.afterDeserialize === "function") { | ||
propDef.afterDeserialize(callback, err, newValue, jsonValue, jsonParentValue, | ||
propNameOrIndex, context, propDef); | ||
} else { | ||
propDef.afterDeserialize(callback, err, newValue, jsonValue, jsonParentValue, propNameOrIndex, context, propDef); | ||
} | ||
else { | ||
callback(err, newValue); | ||
} | ||
} | ||
}; | ||
@@ -834,7 +752,7 @@ /** | ||
* title: true, | ||
* }); | ||
* }) | ||
* createModelSchema(Todo, { | ||
* title: true, | ||
* subTask: object(SubTask), | ||
* }); | ||
* }) | ||
* | ||
@@ -846,7 +764,6 @@ * const todo = deserialize(Todo, { | ||
* }, | ||
* }); | ||
* }) | ||
* | ||
* @param {ModelSchema} modelSchema to be used to (de)serialize the object | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
* @param modelSchema to be used to (de)serialize the object | ||
* @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
*/ | ||
@@ -860,4 +777,4 @@ function object(modelSchema, additionalArgs) { | ||
if (item === null || item === undefined) | ||
return item | ||
return serialize(modelSchema, item) | ||
return item; | ||
return serialize(modelSchema, item); | ||
}, | ||
@@ -868,70 +785,40 @@ deserializer: function (childJson, done, context) { | ||
if (childJson === null || childJson === undefined) | ||
return void done(null, childJson) | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done, additionalArgs) | ||
return void done(null, childJson); | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done, undefined); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
/** | ||
* The `serializeAll` decorator can may used on a class to signal that all primitive properties, | ||
* or complex properties with a name matching a `pattern`, should be serialized automatically. | ||
* | ||
* @example | ||
* @serializeAll class Store { | ||
* a = 3; | ||
* b; | ||
* } | ||
* | ||
* const store = new Store(); | ||
* store.c = 5; | ||
* store.d = {}; | ||
* t.deepEqual(serialize(store), { c: 5 }); | ||
* | ||
* @example | ||
* class DataType { | ||
* @serializable | ||
* x; | ||
* @serializable | ||
* y; | ||
* } | ||
* @serializeAll(/^[a-z]$/, DataType) class ComplexStore { | ||
* } | ||
* | ||
* const store = new ComplexStore(); | ||
* store.a = {x: 1, y: 2}; | ||
* store.b = {}; | ||
* store.somethingElse = 5; | ||
* t.deepEqual(serialize(store), { a: {x: 1, y: 2}, b: { x: undefined, y: undefined } }); | ||
*/ | ||
function serializeAll(targetOrPattern, clazzOrSchema) { | ||
let propSchema; | ||
let invokeImmediately = false; | ||
function serializeAll(targetOrPattern, propertyType) { | ||
var propSchema; | ||
if (arguments.length === 1) { | ||
invariant(typeof targetOrPattern === "function", "@serializeAll can only be used as class decorator"); | ||
propSchema = true; | ||
invokeImmediately = true; | ||
return decorator(targetOrPattern); | ||
} | ||
else { | ||
invariant(typeof targetOrPattern === "object" && targetOrPattern.test, "@serializeAll pattern doesn't have test"); | ||
if (typeof clazzOrSchema === "function") { | ||
clazzOrSchema = object(clazzOrSchema); | ||
if (typeof propertyType === "function") { | ||
propertyType = object(propertyType); | ||
} | ||
invariant(typeof clazzOrSchema === "object" && clazzOrSchema.serializer, "couldn't resolve schema"); | ||
propSchema = Object.assign({}, clazzOrSchema, {pattern: targetOrPattern}); | ||
if (true === propertyType) { | ||
propertyType = _defaultPrimitiveProp; | ||
} | ||
invariant(isPropSchema(propertyType), "couldn't resolve schema"); | ||
propSchema = Object.assign({}, propertyType, { | ||
pattern: targetOrPattern | ||
}); | ||
} | ||
function result(target) { | ||
function decorator(target) { | ||
invariant(typeof target === "function", "@serializeAll can only be used as class decorator"); | ||
var info = getDefaultModelSchema(target); | ||
if (!info || !target.hasOwnProperty("serializeInfo")) { | ||
if (!info) { | ||
info = createModelSchema(target, {}); | ||
setDefaultModelSchema(target, info); | ||
} | ||
getDefaultModelSchema(target).props["*"] = propSchema; | ||
info.props["*"] = propSchema; | ||
return target; | ||
} | ||
if (invokeImmediately) { | ||
return result(targetOrPattern); | ||
} | ||
return result; | ||
return decorator; | ||
} | ||
@@ -942,4 +829,2 @@ | ||
*/ | ||
/** | ||
@@ -960,20 +845,4 @@ * Cancels an asynchronous deserialization or update operation for the specified target object. | ||
*/ | ||
/** | ||
* Similar to deserialize, but updates an existing object instance. | ||
* Properties will always updated entirely, but properties not present in the json will be kept as is. | ||
* Further this method behaves similar to deserialize. | ||
* | ||
* @param {object} modelSchema, optional if it can be inferred from the instance type | ||
* @param {object} target target instance to update | ||
* @param {object} json the json to deserialize | ||
* @param {function} callback the callback to invoke once deserialization has completed. | ||
* @param {*} customArgs custom arguments that are available as `context.args` during the deserialization process. This can be used as dependency injection mechanism to pass in, for example, stores. | ||
* @returns {object|array} deserialized object, possibly incomplete. | ||
*/ | ||
function update(modelSchema, target, json, callback, customArgs) { | ||
var inferModelSchema = | ||
arguments.length === 2 // only target and json | ||
|| typeof arguments[2] === "function"; // callback as third arg | ||
var inferModelSchema = arguments.length === 2 || typeof arguments[2] === "function"; // only target and json // callback as third arg | ||
if (inferModelSchema) { | ||
@@ -985,3 +854,4 @@ target = arguments[0]; | ||
customArgs = arguments[3]; | ||
} else { | ||
} | ||
else { | ||
modelSchema = getDefaultModelSchema(modelSchema); | ||
@@ -991,3 +861,3 @@ } | ||
invariant(typeof target === "object" && target && !Array.isArray(target), "update needs an object"); | ||
var context = new Context(null, modelSchema, json, callback, customArgs); | ||
var context = new Context(undefined, modelSchema, json, callback || GUARDED_NOOP, customArgs); | ||
context.setTarget(target); | ||
@@ -997,50 +867,16 @@ var lock = context.createCallback(GUARDED_NOOP); | ||
lock(); | ||
return result | ||
return result; | ||
} | ||
function defaultRegisterFunction(id, value, context) { | ||
var defaultRegisterFunction = function (id, value, context) { | ||
context.rootContext.resolve(context.modelSchema, id, context.target); | ||
} | ||
/** | ||
* | ||
* | ||
* Similar to primitive, but this field will be marked as the identifier for the given Model type. | ||
* This is used by for example `reference()` to serialize the reference | ||
* | ||
* Identifier accepts an optional `registerFn` with the signature: | ||
* `(id, target, context) => void` | ||
* that can be used to register this object in some store. note that not all fields of this object might | ||
* have been deserialized yet. | ||
* | ||
* @example | ||
* var todos = {}; | ||
* | ||
* var s = _.createSimpleSchema({ | ||
* id: _.identifier((id, object) => (todos[id] = object)), | ||
* title: true, | ||
* }); | ||
* | ||
* _.deserialize(s, { | ||
* id: 1, | ||
* title: 'test0', | ||
* }); | ||
* _.deserialize(s, [{ id: 2, title: 'test2' }, { id: 1, title: 'test1' }]); | ||
* | ||
* t.deepEqual(todos, { | ||
* 1: { id: 1, title: 'test1' }, | ||
* 2: { id: 2, title: 'test2' }, | ||
* }); | ||
* | ||
* @param { RegisterFunction | AdditionalPropArgs } arg1 optional registerFn: function to register this object during creation. | ||
* @param {AdditionalPropArgs} arg2 optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* | ||
* @returns {PropSchema} | ||
*/ | ||
}; | ||
function identifier(arg1, arg2) { | ||
var registerFn, additionalArgs; | ||
var registerFn; | ||
var additionalArgs; | ||
if (typeof arg1 === "function") { | ||
registerFn = arg1; | ||
additionalArgs = arg2; | ||
} else { | ||
} | ||
else { | ||
additionalArgs = arg1; | ||
@@ -1053,3 +889,3 @@ } | ||
deserializer: function (jsonValue, done, context) { | ||
_defaultPrimitiveProp.deserializer(jsonValue, function(err, id) { | ||
_defaultPrimitiveProp.deserializer(jsonValue, function (err, id) { | ||
defaultRegisterFunction(id, context.target, context); | ||
@@ -1059,7 +895,7 @@ if (registerFn) | ||
done(err, id); | ||
}); | ||
}, context); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
@@ -1070,4 +906,3 @@ | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
* @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
*/ | ||
@@ -1077,16 +912,16 @@ function date(additionalArgs) { | ||
var result = { | ||
serializer: function(value) { | ||
serializer: function (value) { | ||
if (value === null || value === undefined) | ||
return value | ||
return value; | ||
invariant(value instanceof Date, "Expected Date object"); | ||
return value.getTime() | ||
return value.getTime(); | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
if (jsonValue === null || jsonValue === undefined) | ||
return void done(null, jsonValue) | ||
return void done(null, new Date(jsonValue)) | ||
return void done(null, jsonValue); | ||
return void done(null, new Date(jsonValue)); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
@@ -1101,14 +936,12 @@ | ||
* title: alias('task', primitive()), | ||
* }); | ||
* }) | ||
* | ||
* console.dir(serialize(new Todo('test'))); | ||
* // { task : "test" } | ||
* serialize(new Todo('test')) // { "task": "test" } | ||
* | ||
* @param {string} name name of the json field to be used for this property | ||
* @param {PropSchema} propSchema propSchema to (de)serialize the contents of this field | ||
* @returns {PropSchema} | ||
* @param name name of the json field to be used for this property | ||
* @param propSchema propSchema to (de)serialize the contents of this field | ||
*/ | ||
function alias(name, propSchema) { | ||
invariant(name && typeof name === "string", "expected prop name as first argument"); | ||
propSchema = (!propSchema || propSchema === true) ? _defaultPrimitiveProp : propSchema; | ||
propSchema = !propSchema || propSchema === true ? _defaultPrimitiveProp : propSchema; | ||
invariant(isPropSchema(propSchema), "expected prop schema as second argument"); | ||
@@ -1120,75 +953,19 @@ invariant(!isAliasedPropSchema(propSchema), "provided prop is already aliased"); | ||
deserializer: propSchema.deserializer, | ||
identifier: isIdentifierPropSchema(propSchema), | ||
identifier: isIdentifierPropSchema(propSchema) || undefined, | ||
beforeDeserialize: propSchema.beforeDeserialize, | ||
afterDeserialize: propSchema.afterDeserialize | ||
} | ||
}; | ||
} | ||
/** | ||
* Can be used to create simple custom propSchema. Multiple things can be done inside of a custom propSchema, like deserializing and serializing other (polymorphic) objects, skipping the serialization of something or checking the context of the obj being (de)serialized. | ||
* The `custom` function takes two parameters, the `serializer` function and the `deserializer` function. | ||
* The `serializer` function has the signature: | ||
* `(value, key, obj) => void` | ||
* When serializing the object `{a: 1}` the `serializer` function will be called with `serializer(1, 'a', {a: 1})`. | ||
* The `deserializer` function has the following signature for synchronous processing | ||
* `(value, context, oldValue) => void` | ||
* For asynchronous processing the function expects the following signature | ||
* `(value, context, oldValue, callback) => void` | ||
* When deserializing the object `{b: 2}` the `deserializer` function will be called with `deserializer(2, contextObj)` ([contextObj reference](https://github.com/mobxjs/serializr#deserialization-context)). | ||
* | ||
* @example | ||
* var schemaDefault = _.createSimpleSchema({ | ||
* a: _.custom( | ||
* function(v) { | ||
* return v + 2; | ||
* }, | ||
* function(v) { | ||
* return v - 2; | ||
* } | ||
* ), | ||
* }); | ||
* t.deepEqual(_.serialize(schemaDefault, { a: 4 }), { a: 6 }); | ||
* t.deepEqual(_.deserialize(schemaDefault, { a: 6 }), { a: 4 }); | ||
* | ||
* var schemaWithAsyncProps = _.createSimpleSchema({ | ||
* a: _.customAsync( | ||
* function(v) { | ||
* return v + 2; | ||
* }, | ||
* function(v, context, oldValue, callback) { | ||
* somePromise(v, context, oldValue).then((result) => { | ||
* callback(null, result - 2) | ||
* }.catch((err) => { | ||
* callback(err) | ||
* } | ||
* } | ||
* ), | ||
* }); | ||
* t.deepEqual(_.serialize(schemaWithAsyncProps, { a: 4 }), { a: 6 }); | ||
* _.deserialize(schemaWithAsyncProps, { a: 6 }, (err, res) => { | ||
* t.deepEqual(res.a, 4) | ||
* }; | ||
* | ||
* @param {function} serializer function that takes a model value and turns it into a json value | ||
* @param {function} deserializer function that takes a json value and turns it into a model value. It also takes context argument, which can allow you to deserialize based on the context of other parameters. | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function custom(serializer, deserializer, additionalArgs) { | ||
invariant(typeof serializer === "function", "first argument should be function"); | ||
invariant((typeof deserializer === "function"), "second argument should be a function or promise"); | ||
invariant(typeof deserializer === "function", "second argument should be a function or promise"); | ||
var result = { | ||
serializer: serializer, | ||
deserializer: function (jsonValue, done, context, oldValue) { | ||
if (deserializer.length === 4) { | ||
deserializer(jsonValue, context, oldValue, done, additionalArgs); | ||
} else { | ||
done(null, deserializer(jsonValue, context, oldValue, null, additionalArgs)); | ||
var result = deserializer(jsonValue, context, oldValue, done); | ||
// FIXME: checking for result === undefined instead of Function.length | ||
// would be nicer, but strictly speaking a breaking change. | ||
if (deserializer.length !== 4) { | ||
done(null, result); | ||
} | ||
@@ -1198,3 +975,3 @@ } | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
@@ -1208,23 +985,21 @@ | ||
* title: optional(primitive()), | ||
* }); | ||
* }) | ||
* | ||
* console.dir(serialize(new Todo())); | ||
* // {} | ||
* serialize(new Todo()) // {} | ||
* | ||
* @param {PropSchema} propSchema propSchema to (de)serialize the contents of this field | ||
* @returns {PropSchema} | ||
* @param propSchema propSchema to (de)serialize the contents of this field | ||
*/ | ||
function optional(name, propSchema) { | ||
propSchema = (!propSchema || propSchema === true) ? _defaultPrimitiveProp : propSchema; | ||
function optional(propSchema) { | ||
propSchema = !propSchema || propSchema === true ? _defaultPrimitiveProp : propSchema; | ||
invariant(isPropSchema(propSchema), "expected prop schema as second argument"); | ||
const propSerializer = propSchema.serializer; | ||
var propSerializer = propSchema.serializer; | ||
invariant(typeof propSerializer === "function", "expected prop schema to have a callable serializer"); | ||
function serializer(...args) { | ||
const result = propSerializer(...args); | ||
var serializer = function (sourcePropertyValue, key, sourceObject) { | ||
var result = propSerializer(sourcePropertyValue, key, sourceObject); | ||
if (result === undefined) { | ||
return SKIP | ||
return SKIP; | ||
} | ||
return result | ||
} | ||
return Object.assign({}, propSchema, {serializer}) | ||
return result; | ||
}; | ||
return Object.assign({}, propSchema, { serializer: serializer }); | ||
} | ||
@@ -1235,75 +1010,21 @@ | ||
context.rootContext.await(modelSchema, uuid, cb); | ||
} | ||
}; | ||
} | ||
/** | ||
* `reference` can be used to (de)serialize references that point to other models. | ||
* | ||
* The first parameter should be either a ModelSchema that has an `identifier()` property (see identifier) | ||
* or a string that represents which attribute in the target object represents the identifier of the object. | ||
* | ||
* The second parameter is a lookup function that is invoked during deserialization to resolve an identifier to | ||
* an object. Its signature should be as follows: | ||
* | ||
* `lookupFunction(identifier, callback, context)` where: | ||
* 1. `identifier` is the identifier being resolved | ||
* 2. `callback` is a node style calblack function to be invoked with the found object (as second arg) or an error (first arg) | ||
* 3. `context` see context. | ||
* | ||
* The lookupFunction is optional. If it is not provided, it will try to find an object of the expected type and required identifier within the same JSON document | ||
* | ||
* N.B. mind issues with circular dependencies when importing model schemas from other files! The module resolve algorithm might expose classes before `createModelSchema` is executed for the target class. | ||
* | ||
* @example | ||
* class User {} | ||
* class Post {} | ||
* | ||
* createModelSchema(User, { | ||
* uuid: identifier(), | ||
* displayname: primitive(), | ||
* }); | ||
* | ||
* createModelSchema(Post, { | ||
* author: reference(User, findUserById), | ||
* message: primitive(), | ||
* }); | ||
* | ||
* function findUserById(uuid, callback) { | ||
* fetch('http://host/user/' + uuid) | ||
* .then(userData => { | ||
* deserialize(User, userData, callback); | ||
* }) | ||
* .catch(callback); | ||
* } | ||
* | ||
* deserialize( | ||
* Post, | ||
* { | ||
* message: 'Hello World', | ||
* author: 234, | ||
* }, | ||
* (err, post) => { | ||
* console.log(post); | ||
* } | ||
* ); | ||
* | ||
* @param target: ModelSchema or string | ||
* @param {RefLookupFunction | AdditionalPropArgs} lookupFn optional function or additionalArgs object | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function reference(target, lookupFn, additionalArgs) { | ||
invariant(!!target, "No modelschema provided. If you are importing it from another file be aware of circular dependencies."); | ||
function reference(target, lookupFnOrAdditionalPropArgs, additionalArgs) { | ||
invariant(!!target, "No modelSchema provided. If you are importing it from another file be aware of circular dependencies."); | ||
var lookupFn = "function" === typeof lookupFnOrAdditionalPropArgs | ||
? lookupFnOrAdditionalPropArgs | ||
: undefined; | ||
additionalArgs = | ||
additionalArgs || | ||
(lookupFn ? undefined : lookupFnOrAdditionalPropArgs); | ||
var initialized = false; | ||
var childIdentifierAttribute; | ||
if (typeof lookupFn === "object" && additionalArgs === undefined) { | ||
additionalArgs = lookupFn; | ||
lookupFn = undefined; | ||
} | ||
function initialize() { | ||
initialized = true; | ||
invariant(typeof target !== "string" || lookupFn && typeof lookupFn === "function", "if the reference target is specified by attribute name, a lookup function is required"); | ||
invariant(typeof target !== "string" || typeof lookupFn === "function", "if the reference target is specified by attribute name, a lookup function is required"); | ||
invariant(!lookupFn || typeof lookupFn === "function", "second argument should be a lookup function or additional arguments object"); | ||
if (typeof target === "string") | ||
if (typeof target === "string") { | ||
childIdentifierAttribute = target; | ||
} | ||
else { | ||
@@ -1321,5 +1042,5 @@ var modelSchema = getDefaultModelSchema(target); | ||
initialize(); | ||
return item ? item[childIdentifierAttribute] : null | ||
return item ? item[childIdentifierAttribute] : null; | ||
}, | ||
deserializer: function(identifierValue, done, context) { | ||
deserializer: function (identifierValue, done, context) { | ||
if (!initialized) | ||
@@ -1334,3 +1055,3 @@ initialize(); | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
@@ -1349,7 +1070,7 @@ | ||
* title: true, | ||
* }); | ||
* }) | ||
* createModelSchema(Todo, { | ||
* title: true, | ||
* subTask: list(object(SubTask)), | ||
* }); | ||
* }) | ||
* | ||
@@ -1363,7 +1084,6 @@ * const todo = deserialize(Todo, { | ||
* ], | ||
* }); | ||
* }) | ||
* | ||
* @param {PropSchema} propSchema to be used to (de)serialize the contents of the array | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
* @param propSchema to be used to (de)serialize the contents of the array | ||
* @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
*/ | ||
@@ -1373,16 +1093,14 @@ function list(propSchema, additionalArgs) { | ||
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); | ||
invariant(!isAliasedPropSchema(propSchema), | ||
"provided prop is aliased, please put aliases first"); | ||
invariant(!isAliasedPropSchema(propSchema), "provided prop is aliased, please put aliases first"); | ||
var result = { | ||
serializer: function (ar) { | ||
if (ar === undefined) { | ||
return SKIP | ||
return SKIP; | ||
} | ||
invariant(ar && "length" in ar && "map" in ar, "expected array (like) object"); | ||
return ar.map(propSchema.serializer) | ||
return ar.map(propSchema.serializer); | ||
}, | ||
deserializer: function (jsonArray, done, context) { | ||
if (!Array.isArray(jsonArray)) | ||
return void done("[serializr] expected JSON array") | ||
return void done("[serializr] expected JSON array"); | ||
function processItem(jsonValue, onItemDone, itemIndex) { | ||
@@ -1392,43 +1110,22 @@ function callbackBefore(err, value) { | ||
propSchema.deserializer(value, deserializeDone, context); | ||
} else { | ||
} | ||
else { | ||
onItemDone(err); | ||
} | ||
} | ||
function deserializeDone(err, value) { | ||
if (typeof propSchema.afterDeserialize === "function") { | ||
onAfterDeserialize(callbackAfter, err, value, jsonValue, itemIndex, context, | ||
propSchema); | ||
} else { | ||
onAfterDeserialize(onItemDone, err, value, jsonValue, jsonArray, itemIndex, context, propSchema); | ||
} | ||
else { | ||
onItemDone(err, value); | ||
} | ||
} | ||
function callbackAfter(errPreliminary, finalOrRetryValue) { | ||
if (errPreliminary && finalOrRetryValue !== undefined && | ||
typeof propSchema.afterDeserialize === "function") { | ||
propSchema.deserializer( | ||
finalOrRetryValue, | ||
deserializeDone, | ||
context | ||
); | ||
} else { | ||
onItemDone(errPreliminary, finalOrRetryValue); | ||
} | ||
} | ||
onBeforeDeserialize(callbackBefore, jsonValue, jsonArray, itemIndex, context, | ||
propSchema); | ||
onBeforeDeserialize(callbackBefore, jsonValue, jsonArray, itemIndex, context, propSchema); | ||
} | ||
parallel( | ||
jsonArray, | ||
processItem, | ||
done | ||
); | ||
parallel(jsonArray, processItem, done); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
@@ -1441,5 +1138,3 @@ | ||
* | ||
* @param {*} propSchema | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
* @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
*/ | ||
@@ -1450,49 +1145,46 @@ function map(propSchema, additionalArgs) { | ||
invariant(!isAliasedPropSchema(propSchema), "provided prop is aliased, please put aliases first"); | ||
var res = { | ||
var result = { | ||
serializer: function (m) { | ||
invariant(m && typeof m === "object", "expected object or Map"); | ||
var isMap = isMapLike(m); | ||
var result = {}; | ||
if (isMap) | ||
m.forEach(function (value, key) { | ||
result[key] = propSchema.serializer(value); | ||
}); | ||
else for (var key in m) | ||
result[key] = propSchema.serializer(m[key]); | ||
return result | ||
if (isMapLike(m)) { | ||
m.forEach(function (value, key) { return (result[key] = propSchema.serializer(value, key, m)); }); | ||
} | ||
else { | ||
for (var key in m) | ||
result[key] = propSchema.serializer(m[key], key, m); | ||
} | ||
return result; | ||
}, | ||
deserializer: function (jsonObject, done, context, oldValue) { | ||
if (!jsonObject || typeof jsonObject !== "object") | ||
return void done("[serializr] expected JSON object") | ||
return void done("[serializr] expected JSON object"); | ||
var keys = Object.keys(jsonObject); | ||
list(propSchema, additionalArgs).deserializer( | ||
keys.map(function (key) { | ||
return jsonObject[key] | ||
}), | ||
function (err, values) { | ||
if (err) | ||
return void done(err) | ||
var isMap = isMapLike(oldValue); | ||
var newValue; | ||
if (isMap) { | ||
// if the oldValue is a map, we recycle it | ||
// there are many variations and this way we don't have to | ||
// know about the original constructor | ||
oldValue.clear(); | ||
newValue = oldValue; | ||
} else | ||
newValue = {}; | ||
for (var i = 0, l = keys.length; i < l; i++) | ||
if (isMap) | ||
newValue.set(keys[i], values[i]); | ||
else | ||
newValue[keys[i]] = values[i]; | ||
done(null, newValue); | ||
}, | ||
context | ||
); | ||
list(propSchema, additionalArgs).deserializer(keys.map(function (key) { | ||
return jsonObject[key]; | ||
}), function (err, values) { | ||
if (err) | ||
return void done(err); | ||
var isMap = isMapLike(oldValue); | ||
var newValue; | ||
if (isMap) { | ||
// if the oldValue is a map, we recycle it | ||
// there are many variations and this way we don't have to | ||
// know about the original constructor | ||
oldValue.clear(); | ||
newValue = oldValue; | ||
} | ||
else | ||
newValue = {}; | ||
for (var i = 0, l = keys.length; i < l; i++) | ||
if (isMap) | ||
newValue.set(keys[i], values[i]); | ||
else | ||
newValue[keys[i]] = values[i]; | ||
done(null, newValue); | ||
}, context); | ||
} | ||
}; | ||
res = processAdditionalPropArgs(res, additionalArgs); | ||
return res | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result; | ||
} | ||
@@ -1509,6 +1201,4 @@ | ||
* | ||
* @param {any} propSchema | ||
* @param {string} keyPropertyName - the property of stored objects used as key in the map | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
* @param keyPropertyName - the property of stored objects used as key in the map | ||
* @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
*/ | ||
@@ -1519,45 +1209,40 @@ function mapAsArray(propSchema, keyPropertyName, additionalArgs) { | ||
invariant(!!keyPropertyName, "expected key property name as second argument"); | ||
var res = { | ||
var result = { | ||
serializer: function (m) { | ||
invariant(m && typeof m === "object", "expected object or Map"); | ||
var isMap = isMapLike(m); | ||
var result = []; | ||
// eslint-disable-next-line no-unused-vars | ||
if (isMap) { | ||
m.forEach(function (value) { | ||
result.push(propSchema.serializer(value)); | ||
}); | ||
} else for (var key in m) { | ||
result.push(propSchema.serializer(m[key])); | ||
// result[key] = propSchema.serializer(m[key]) | ||
if (isMapLike(m)) { | ||
m.forEach(function (value, key) { return result.push(propSchema.serializer(value, key, m)); }); | ||
} | ||
return result | ||
else { | ||
for (var key in m) | ||
result.push(propSchema.serializer(m[key], key, m)); | ||
} | ||
return result; | ||
}, | ||
deserializer: function (jsonArray, done, context, oldValue) { | ||
list(propSchema, additionalArgs).deserializer( | ||
jsonArray, | ||
function (err, values) { | ||
if (err) | ||
return void done(err) | ||
var isMap = isMapLike(oldValue); | ||
var newValue; | ||
if (isMap) { | ||
oldValue.clear(); | ||
newValue = oldValue; | ||
} else { | ||
newValue = {}; | ||
} | ||
for (var i = 0, l = jsonArray.length; i < l; i++) | ||
if (isMap) | ||
newValue.set(values[i][keyPropertyName], values[i]); | ||
else | ||
newValue[values[i][keyPropertyName].toString()] = values[i]; | ||
done(null, newValue); | ||
}, | ||
context | ||
); | ||
list(propSchema, additionalArgs).deserializer(jsonArray, function (err, values) { | ||
if (err) | ||
return void done(err); | ||
var oldValueIsMap = isMapLike(oldValue); | ||
var newValue; | ||
if (oldValueIsMap) { | ||
oldValue.clear(); | ||
newValue = oldValue; | ||
} | ||
else { | ||
newValue = {}; | ||
} | ||
for (var i = 0, l = jsonArray.length; i < l; i++) | ||
if (oldValueIsMap) | ||
newValue.set(values[i][keyPropertyName], values[i]); | ||
else | ||
newValue[values[i][keyPropertyName].toString()] = values[i]; | ||
done(null, newValue); | ||
}, context, undefined); | ||
} | ||
}; | ||
res = processAdditionalPropArgs(res, additionalArgs); | ||
return res | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result; | ||
} | ||
@@ -1572,9 +1257,8 @@ | ||
* rawData: raw(), | ||
* }); | ||
* }) | ||
* | ||
* console.dir(serialize(new Model({ rawData: { a: 1, b: [], c: {} } } }))); | ||
* // outputs: { rawData: { a: 1, b: [], c: {} } } } | ||
* serialize(new Model({ rawData: { a: 1, b: [], c: {} } } })) | ||
* // { "rawData": { a: 1, b: [], c: {} } } } | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
* @param additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
*/ | ||
@@ -1584,17 +1268,13 @@ function raw(additionalArgs) { | ||
serializer: function (value) { | ||
return value | ||
return value; | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
return void done(null, jsonValue) | ||
return void done(null, jsonValue); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
return result; | ||
} | ||
/* | ||
* ## Managing model schemas | ||
*/ | ||
// ~ deprecated | ||
export { createSimpleSchema, createModelSchema, getDefaultModelSchema, setDefaultModelSchema, serializable, serialize, serializeAll, cancelDeserialize, deserialize, update, primitive, identifier, date, alias, custom, object, object as child, optional, reference, reference as ref, list, map, mapAsArray, raw, SKIP }; | ||
export { SKIP, alias, cancelDeserialize, createModelSchema, createSimpleSchema, custom, date, deserialize, getDefaultModelSchema, identifier, list, map, mapAsArray, object, optional, primitive, raw, reference, serializable, serialize, serializeAll, setDefaultModelSchema, update }; | ||
//# sourceMappingURL=serializr.js.map |
/** serializr - (c) Michel Weststrate 2016 - MIT Licensed */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define("serializr",["exports"],t):t((e=e||self).serializr={})}(this,function(e){"use strict";var t={j:function(e){try{return JSON.stringify(e)}catch(t){return"[UnexpectedJSONParseError]: "+t.message}}};function n(e,n){if(!e){var r=Array.prototype.slice.call(arguments,2),i=[],o=0,a=n.replace(/%([a-zA-Z%])/g,function(e,n){if("%%"===e)return e;var a=t[n];if("function"==typeof a){var s=r[o++];return i.push(s),a(s)}return e});throw console&&i.length>0&&console.log.apply(console,i),new Error("[serializr] "+(a||"Illegal State"))}}function r(e){if(e)throw new Error(e)}function i(e,t,n){if(0!==e.length){var r=e.filter(function(){return!0}).length,i=[],o=!1;e.forEach(function(e,a){t(e,function(e,t,a){t?o||(o=!0,n(t)):(i[e]=a,0==--r&&n(null,i))}.bind(null,a),a)})}else n(null,[])}function o(e){return null===e||"object"!=typeof e&&"function"!=typeof e}function a(e){return e&&e.factory&&e.props}function s(e){return e&&e.serializer&&e.deserializer}function f(e){return"object"==typeof e&&!!e.jsonname}function c(e){return"object"==typeof e&&!0===e.identifier}function u(e,t){for(;e;){if(e===t)return!0;e=e["extends"]}return!1}function l(e){return e&&"function"==typeof e.keys&&"function"==typeof e.clear}function p(e,t){if(t){n(s(e),"expected a propSchema");["beforeDeserialize","afterDeserialize"].forEach(function(n){"function"==typeof t[n]&&(e[n]=t[n])})}return e}function d(e){return e?a(e)?e:a(e.serializeInfo)?e.serializeInfo:e.constructor&&e.constructor.serializeInfo?e.constructor.serializeInfo:void 0:null}function h(e,t){return n(a(t)),e.serializeInfo=t}function y(e,t,r){n(e!==Object,"one cannot simply put define a model schema for Object"),n("function"==typeof e,"expected constructor function");var i={targetClass:e,factory:r||function(){return new e},props:t};if(e.prototype.constructor!==Object){var o=d(e.prototype.constructor);o&&o.targetClass!==e&&(i["extends"]=o)}return h(e,i),i}function m(e){var t={serializer:function(e){return n(o(e),"this value is not primitive: "+e),e},deserializer:function(e,t){o(e)?t(null,e):t("[serializr] this value is not primitive: "+e)}};return t=p(t,e)}var g="undefined"!=typeof Symbol?Symbol("SKIP"):{SKIP:!0},b=m(),v=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,z=/([^\s,]+)/g;function j(e,t,r,i){var o,a,f;if(n(arguments.length>=2,"too few arguments. Please use @serializable as property decorator"),r===undefined&&"function"==typeof t&&t.prototype&&i!==undefined&&"number"==typeof i){n(s(e),"Constructor params must use alias(name)"),n(e.jsonname,"Constructor params must use alias(name)");var c=(a=t.toString().replace(v,""),null===(f=a.slice(a.indexOf("(")+1,a.indexOf(")")).match(z))&&(f=[]),f);c.length>=i&&(r=c[i],e.paramNumber=i,i=undefined,t=t.prototype,o=function(e){for(var n=[],r=0;r<t.constructor.length;r++)Object.keys(e.modelSchema.props).forEach(function(t){var i=e.modelSchema.props[t];i.paramNumber===r&&(n[r]=e.json[i.jsonname])});return new(Function.prototype.bind.apply(t.constructor,[null].concat(n)))})}n("string"==typeof r,"incorrect usage of @serializable decorator");var u=d(t);return u&&t.constructor.hasOwnProperty("serializeInfo")||(u=y(t.constructor,{},o)),u&&u.targetClass!==t.constructor&&(u=y(t.constructor,{},o)),u.props[r]=e,!i||i.get||i.set||(i.writable=!0),i}function x(e,t){n(1===arguments.length||2===arguments.length,"serialize expects one or 2 arguments");var r=1===arguments.length?e:t,i=1===arguments.length?null:e;if(Array.isArray(r)){if(0===r.length)return[];i?"object"!=typeof i&&(i=d(i)):i=d(r[0])}else i?"object"!=typeof i&&(i=d(i)):i=d(r);return n(!!i,"Failed to find default schema for "+e),Array.isArray(r)?r.map(function(e){return C(i,e)}):C(i,r)}function R(e){n(!0===e||e.pattern,"prop schema '*' can only be used with 'true' or a prop def with a 'pattern': "+JSON.stringify(e))}function C(e,t){var r;return n(e&&"object"==typeof e&&e.props,"Expected schema"),n(t&&"object"==typeof t,"Expected object"),r=e["extends"]?C(e["extends"],t):{},Object.keys(e.props).forEach(function(n){var i=e.props[n];if("*"!==n){if(!0===i&&(i=b),!1!==i){var a=i.serializer(t[n],n,t);a!==g&&(r[i.jsonname||n]=a)}}else!function(e,t,n,r){for(var i in R(t),n)if(n.hasOwnProperty(i)&&!(i in e.props)&&(!0===t||t.pattern&&t.pattern.test(i))){var a=n[i];if(!0===t)o(a)&&(r[i]=a);else if(t.props){var s=x(t,a);if(s===g)return;r[i]=s}else{var s=t.serializer(a,i,n);if(s===g)return;r[i]=s}}}(e,i,t,r)}),r}var S=new WeakMap;function w(e,t,n,i,o){this.parentContext=e,this.isRoot=!e,this.pendingCallbacks=0,this.pendingRefsCount=0,this.onReadyCb=i||r,this.json=n,this.target=null,this.hasError=!1,this.modelSchema=t,this.isRoot?(this.rootContext=this,this.args=o,this.pendingRefs={},this.resolvedRefs={}):(this.rootContext=e.rootContext,this.args=e.args)}function k(e,t){for(var n in e.props)if("object"==typeof e.props[n]&&e.props[n].jsonname===t)return!0;return!1}function A(e,t,i,o,a){if(null!==i&&i!==undefined&&"object"==typeof i){var s=new w(e,t,i,o,a),f=t.factory(s);n(!!f,"No object returned from factory"),s.setTarget(f);var c=s.createCallback(r);return O(s,t,i,f),c(),f}o(null,null)}function O(e,t,i,a){t["extends"]&&O(e,t["extends"],i,a),Object.keys(t.props).forEach(function(s){var f=t.props[s];if("*"!==s){if(!0===f&&(f=b),!1!==f){var c=f.jsonname||s;E(function(t,n){t||n===undefined||function(t,n,r){t.deserializer(n,function o(s){return function(f,c){D(function(n,i){n&&i!==undefined&&"function"==typeof t.afterDeserialize?t.deserializer(i,o(s),e,a[r]):s(n,i)},f,c,n,i,r,e,t)}}(e.rootContext.createCallback(function(e){e!==g&&(a[r]=e)})),e,a[r])}(f,n,s)},i[c],i,c,e,f)}}else!function(e,t,i,a,s){for(var f in R(i),s)if(!(f in t.props||k(t,f))){var c=s[f];if(!0===i)n(o(c),"encountered non primitive value while deserializing '*' properties in property '"+f+"': "+c),a[f]=c;else if(i.pattern.test(f))if(i.factory){var u=A(e,i,c,e.callback||r,{});u!==undefined&&(a[f]=u)}else{function l(e){e!==g&&(a[f]=e)}i.deserializer(c,e.rootContext.createCallback(l),e)}}}(e,t,f,a,i)})}function E(e,t,n,r,i,o){o&&"function"==typeof o.beforeDeserialize?o.beforeDeserialize(e,t,n,r,i,o):e(null,t)}function D(e,t,n,r,i,o,a,s){s&&"function"==typeof s.afterDeserialize?s.afterDeserialize(e,t,n,r,i,o,a,s):e(t,n)}function I(e,t){n("object"==typeof e||"function"==typeof e,"No modelschema provided. If you are importing it from another file be aware of circular dependencies.");var r={serializer:function(t){return n(a(e=d(e)),"expected modelSchema, got "+e),null===t||t===undefined?t:x(e,t)},deserializer:function(r,i,o){n(a(e=d(e)),"expected modelSchema, got "+e),null!==r&&r!==undefined?A(o,e,r,i,t):i(null,r)}};return r=p(r,t)}function N(e,t,r){n(!!e,"No modelschema provided. If you are importing it from another file be aware of circular dependencies.");var i,o=!1;function s(){if(o=!0,n("string"!=typeof e||t&&"function"==typeof t,"if the reference target is specified by attribute name, a lookup function is required"),n(!t||"function"==typeof t,"second argument should be a lookup function or additional arguments object"),"string"==typeof e)i=e;else{var r=d(e);n(a(r),"expected model schema or string as first argument for 'ref', got "+r),t=t||function(e){return function(t,n,r){r.rootContext.await(e,t,n)}}(r),i=function(e){for(n(a(e));e;){for(var t in e.props)if("object"==typeof e.props[t]&&!0===e.props[t].identifier)return t;e=e["extends"]}return null}(r),n(!!i,"provided model schema doesn't define an identifier() property and cannot be used by 'ref'.")}}"object"==typeof t&&r===undefined&&(r=t,t=undefined);var f={serializer:function(e){return o||s(),e?e[i]:null},deserializer:function(e,n,r){o||s(),null===e||e===undefined?n(null,e):t(e,n,r)}};return f=p(f,r)}function P(e,t){n(s(e=e||b),"expected prop schema as first argument"),n(!f(e),"provided prop is aliased, please put aliases first");var r={serializer:function(t){return t===undefined?g:(n(t&&"length"in t&&"map"in t,"expected array (like) object"),t.map(e.serializer))},deserializer:function(t,n,r){Array.isArray(t)?i(t,function(n,i,o){function a(t,a){"function"==typeof e.afterDeserialize?D(s,t,a,n,o,r,e):i(t,a)}function s(t,n){t&&n!==undefined&&"function"==typeof e.afterDeserialize?e.deserializer(n,a,r):i(t,n)}E(function(t,n){t?i(t):e.deserializer(n,a,r)},n,t,o,r,e)},n):n("[serializr] expected JSON array")}};return r=p(r,t)}w.prototype.createCallback=function(e){return this.pendingCallbacks++,function(e){var t=!1;return function(){if(!t)return t=!0,e.apply(null,arguments);n(!1,"callback was invoked twice")}}(function(t,n){t?this.hasError||(this.hasError=!0,this.onReadyCb(t),S["delete"](this)):this.hasError||(e(n),--this.pendingCallbacks===this.pendingRefsCount&&(this.pendingRefsCount>0?(this.onReadyCb(new Error('Unresolvable references in json: "'+Object.keys(this.pendingRefs).filter(function(e){return this.pendingRefs[e].length>0},this).join('", "')+'"')),S["delete"](this)):(this.onReadyCb(null,this.target),S["delete"](this))))}.bind(this))},w.prototype.await=function(e,t,r){if(n(this.isRoot),t in this.resolvedRefs){var i=this.resolvedRefs[t].filter(function(t){return u(t.modelSchema,e)})[0];if(i)return void r(null,i.value)}this.pendingRefsCount++,this.pendingRefs[t]||(this.pendingRefs[t]=[]),this.pendingRefs[t].push({modelSchema:e,uuid:t,callback:r})},w.prototype.resolve=function(e,t,r){if(n(this.isRoot),this.resolvedRefs[t]||(this.resolvedRefs[t]=[]),this.resolvedRefs[t].push({modelSchema:e,value:r}),t in this.pendingRefs)for(var i=this.pendingRefs[t].length-1;i>=0;i--){var o=this.pendingRefs[t][i];u(e,o.modelSchema)&&(this.pendingRefs[t].splice(i,1),this.pendingRefsCount--,o.callback(null,r))}},w.prototype.setTarget=function(e){this.isRoot&&this.target&&S["delete"](this.target),this.target=e,S.set(this.target,this)},w.prototype.cancelAwaits=function(){n(this.isRoot);var e=this;Object.keys(this.pendingRefs).forEach(function(t){e.pendingRefs[t].forEach(function(n){e.pendingRefsCount--,n.callback(new Error("Reference resolution canceled for "+t))})}),this.pendingRefs={},this.pendingRefsCount=0},e.createSimpleSchema=function(e){return{factory:function(){return{}},props:e}},e.createModelSchema=y,e.getDefaultModelSchema=d,e.setDefaultModelSchema=h,e.serializable=function(e,t,r){if(1===arguments.length){var i=!0===e?b:e;return n(s(i),"@serializable expects prop schema"),j.bind(null,i)}return j(m(),e,t,r)},e.serialize=x,e.serializeAll=function(e,t){let r,i=!1;function o(e){var t=d(e);return t&&e.hasOwnProperty("serializeInfo")||h(e,t=y(e,{})),d(e).props["*"]=r,e}return 1===arguments.length?(n("function"==typeof e,"@serializeAll can only be used as class decorator"),r=!0,i=!0):(n("object"==typeof e&&e.test,"@serializeAll pattern doesn't have test"),"function"==typeof t&&(t=I(t)),n("object"==typeof t&&t.serializer,"couldn't resolve schema"),r=Object.assign({},t,{pattern:e})),i?o(e):o},e.cancelDeserialize=function(e){n("object"==typeof e&&e&&!Array.isArray(e),"cancelDeserialize needs an object");var t,r=(t=e,S.get(t));r&&r.cancelAwaits()},e.deserialize=function(e,t,o,s){if(n(arguments.length>=2,"deserialize expects at least 2 arguments"),n(a(e=d(e)),"first argument should be model schema"),Array.isArray(t)){var f=[];return i(t,function(t,n){var r=A(null,e,t,n,s);f.push(r)},o||r),f}return A(null,e,t,o,s)},e.update=function(e,t,i,o,s){2===arguments.length||"function"==typeof arguments[2]?(e=d(t=arguments[0]),i=arguments[1],o=arguments[2],s=arguments[3]):e=d(e),n(a(e),"update failed to determine schema"),n("object"==typeof t&&t&&!Array.isArray(t),"update needs an object");var f=new w(null,e,i,o,s);f.setTarget(t);var c=f.createCallback(r),u=O(f,e,i,t);return c(),u},e.primitive=m,e.identifier=function(e,t){var r,i;"function"==typeof e?(r=e,i=t):i=e,n(!i||"object"==typeof i,"Additional property arguments should be an object, register function should be omitted or a funtion");var o={identifier:!0,serializer:b.serializer,deserializer:function(e,t,n){b.deserializer(e,function(e,i){!function(e,t,n){n.rootContext.resolve(n.modelSchema,e,n.target)}(i,n.target,n),r&&r(i,n.target,n),t(e,i)})}};return o=p(o,i)},e.date=function(e){var t={serializer:function(e){return null===e||e===undefined?e:(n(e instanceof Date,"Expected Date object"),e.getTime())},deserializer:function(e,t){null!==e&&e!==undefined?t(null,new Date(e)):t(null,e)}};return t=p(t,e)},e.alias=function(e,t){return n(e&&"string"==typeof e,"expected prop name as first argument"),n(s(t=t&&!0!==t?t:b),"expected prop schema as second argument"),n(!f(t),"provided prop is already aliased"),{jsonname:e,serializer:t.serializer,deserializer:t.deserializer,identifier:c(t),beforeDeserialize:t.beforeDeserialize,afterDeserialize:t.afterDeserialize}},e.custom=function(e,t,r){n("function"==typeof e,"first argument should be function"),n("function"==typeof t,"second argument should be a function or promise");var i={serializer:e,deserializer:function(e,n,i,o){4===t.length?t(e,i,o,n,r):n(null,t(e,i,o,null,r))}};return i=p(i,r)},e.object=I,e.child=I,e.optional=function(e,t){n(s(t=t&&!0!==t?t:b),"expected prop schema as second argument");const r=t.serializer;return n("function"==typeof r,"expected prop schema to have a callable serializer"),Object.assign({},t,{serializer:function(...e){const t=r(...e);return t===undefined?g:t}})},e.reference=N,e.ref=N,e.list=P,e.map=function(e,t){n(s(e=e||b),"expected prop schema as first argument"),n(!f(e),"provided prop is aliased, please put aliases first");var r={serializer:function(t){n(t&&"object"==typeof t,"expected object or Map");var r=l(t),i={};if(r)t.forEach(function(t,n){i[n]=e.serializer(t)});else for(var o in t)i[o]=e.serializer(t[o]);return i},deserializer:function(n,r,i,o){if(n&&"object"==typeof n){var a=Object.keys(n);P(e,t).deserializer(a.map(function(e){return n[e]}),function(e,t){if(e)r(e);else{var n,i=l(o);i?(o.clear(),n=o):n={};for(var s=0,f=a.length;s<f;s++)i?n.set(a[s],t[s]):n[a[s]]=t[s];r(null,n)}},i)}else r("[serializr] expected JSON object")}};return r=p(r,t)},e.mapAsArray=function(e,t,r){n(s(e=e||b),"expected prop schema as first argument"),n(!!t,"expected key property name as second argument");var i={serializer:function(t){n(t&&"object"==typeof t,"expected object or Map");var r=l(t),i=[];if(r)t.forEach(function(t){i.push(e.serializer(t))});else for(var o in t)i.push(e.serializer(t[o]));return i},deserializer:function(n,i,o,a){P(e,r).deserializer(n,function(e,r){if(e)i(e);else{var o,s=l(a);s?(a.clear(),o=a):o={};for(var f=0,c=n.length;f<c;f++)s?o.set(r[f][t],r[f]):o[r[f][t].toString()]=r[f];i(null,o)}},o)}};return i=p(i,r)},e.raw=function(e){var t={serializer:function(e){return e},deserializer:function(e,t){t(null,e)}};return t=p(t,e)},e.SKIP=g,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
var e,n;e=this,n=function(e){"use strict";var n={j:function(e){try{return JSON.stringify(e)}catch(n){return"[UnexpectedJSONParseError]: "+n.message}},l:function(e){return e.toString()}};function t(e,t){for(var r=[],i=2;i<arguments.length;i++)r[i-2]=arguments[i];if(!e){var o=[],u=0,f=t.replace(/%([a-zA-Z%])/g,(function(e,t){if("%%"===e)return e;var i=n[t];if("function"==typeof i){var f=r[u++];return o.push(f),i(f)}return e}));throw console&&o.length>0&&console.log.apply(console,o),Error("[serializr] "+(f||"Illegal State"))}}function r(e){if(e)throw Error(e)}function i(e,n,t){if(0!==e.length){var r=e.filter((function(e){return!0})).length,i=[],o=!1;e.forEach((function(e,u){n(e,(function(e,n){e?o||(o=!0,t(e)):(i[u]=n,0==--r&&t(null,i))}),u)}))}else t(null,[])}function o(e){return null===e||"object"!=typeof e&&"function"!=typeof e}function u(e){return e&&e.factory&&e.props}function f(e){return e&&e.serializer&&e.deserializer}function a(e){return"object"==typeof e&&"string"==typeof e.jsonname}function c(e){return"object"==typeof e&&!0===e.identifier}function s(e,n){for(var t=e;t;){if(t===n)return!0;t=t["extends"]}return!1}function l(e){return e&&"function"==typeof e.keys&&"function"==typeof e.clear}function d(e,n){return n&&(t(f(e),"expected a propSchema"),Object.assign(e,n)),e}function p(e){return e?u(e)?e:u(e.serializeInfo)?e.serializeInfo:e.constructor&&e.constructor.serializeInfo?e.constructor.serializeInfo:void 0:undefined}function v(e,n){return t(u(n),"expected modelSchema, got "+n),e.serializeInfo=n,n}function h(e,n,r){t(e!==Object,"one cannot simply put define a model schema for Object"),t("function"==typeof e,"expected constructor function");var i={targetClass:e,factory:r||function(){return new e},props:n};if(e.prototype.constructor!==Object){var o=p(e.prototype.constructor);o&&o.targetClass!==e&&(i["extends"]=o)}return v(e,i),i} | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */function m(){for(var e=0,n=0,t=arguments.length;n<t;n++)e+=arguments[n].length;var r=Array(e),i=0;for(n=0;n<t;n++)for(var o=arguments[n],u=0,f=o.length;u<f;u++,i++)r[i]=o[u];return r}function y(e){return d({serializer:function(e){return t(o(e),"this value is not primitive: "+e),e},deserializer:function(e,n){o(e)?n(null,e):n("[serializr] this value is not primitive: "+e)}},e)}var b="undefined"!=typeof Symbol?Symbol("SKIP"):{SKIP:!0},j=y(),z=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,g=/([^\s,]+)/g;function x(e){var n,t=e.toString().replace(z,"");return null!=(n=t.slice(t.indexOf("(")+1,t.indexOf(")")).match(g))?n:[]}function S(e,n,r,i){var o;if(t(arguments.length>=2,"too few arguments. Please use @serializable as property decorator"),r===undefined&&"function"==typeof n&&n.prototype&&i!==undefined&&"number"==typeof i){t(f(e),"Constructor params must use alias(name)"),t(a(e),"Constructor params must use alias(name)");var u=x(n);u.length>=i&&(r=u[i],e.paramNumber=i,i=undefined,n=n.prototype,o=function(e){for(var t,r=[],i=function(n){Object.keys(e.modelSchema.props).forEach((function(t){var i=e.modelSchema.props[t];i.paramNumber===n&&(r[n]=e.json[i.jsonname])}))},o=0;o<n.constructor.length;o++)i(o);return(t=n.constructor).bind.apply(t,m([undefined],r))})}t("string"==typeof r,"incorrect usage of @serializable decorator");var c=p(n);return c&&n.constructor.hasOwnProperty("serializeInfo")||(c=h(n.constructor,{},o)),c&&c.targetClass!==n.constructor&&(c=h(n.constructor,{},o)),c.props[r]=e,!i||i.get||i.set||(i.writable=!0),i}function O(e,n){t(1===arguments.length||2===arguments.length,"serialize expects one or 2 arguments");var r=null!=n?n:e,i=n&&e;if(Array.isArray(r)){if(0===r.length)return[];i?"object"!=typeof i&&(i=p(i)):i=p(r[0])}else i?"object"!=typeof i&&(i=p(i)):i=p(r);var o=i;return t(o,"Failed to find default schema for "+e),Array.isArray(r)?r.map((function(e){return w(o,e)})):w(o,r)}function w(e,n){var r;return t(e&&"object"==typeof e&&e.props,"Expected schema"),t(n&&"object"==typeof n,"Expected object"),r=e["extends"]?w(e["extends"],n):{},Object.keys(e.props).forEach((function(t){var i=e.props[t];if(i)if("*"!==t){!0===i&&(i=j);var u=i.serializer(n[t],t,n);u!==b&&(r[i.jsonname||t]=u)}else!function(e,n,t,r){for(var i=0,u=Object.keys(t);i<u.length;i++){var f=u[i];if(!(f in e.props)&&(!0===n||n&&(!n.pattern||n.pattern.test(f)))){var a=t[f];if(!0===n)o(a)&&(r[f]=a);else{var c=n.serializer(a,f,t);if(c===b)return;r[f]=c}}}}(e,i,n,r)})),r}var A=new WeakMap,k=function(){function e(e,n,t,r,i){this.parentContext=e,this.modelSchema=n,this.json=t,this.onReadyCb=r,this.isRoot=!e,this.pendingCallbacks=0,this.pendingRefsCount=0,this.target=undefined,this.hasError=!1,e?(this.rootContext=e.rootContext,this.args=e.args):(this.rootContext=this,this.args=i,this.pendingRefs={},this.resolvedRefs={})}return e.prototype.createCallback=function(e){var n=this;return this.pendingCallbacks++,function(e){var n=!1;return function(){if(!n)return n=!0,e.apply(null,arguments);t(!1,"callback was invoked twice")}}((function(t,r){t?n.hasError||(n.hasError=!0,n.onReadyCb(t),A["delete"](n)):n.hasError||(e(r),--n.pendingCallbacks===n.pendingRefsCount&&(n.pendingRefsCount>0?(n.onReadyCb(Error('Unresolvable references in json: "'+Object.keys(n.pendingRefs).filter((function(e){return n.pendingRefs[e].length>0})).join('", "')+'"')),A["delete"](n)):(n.onReadyCb(null,n.target),A["delete"](n))))}))},e.prototype.await=function(e,n,r){if(t(this.isRoot,"await can only be called on the root context"),n in this.resolvedRefs){var i=this.resolvedRefs[n].filter((function(n){return s(n.modelSchema,e)}))[0];if(i)return void r(null,i.value)}this.pendingRefsCount++,this.pendingRefs[n]||(this.pendingRefs[n]=[]),this.pendingRefs[n].push({modelSchema:e,uuid:n,callback:r})},e.prototype.resolve=function(e,n,r){if(t(this.isRoot,"resolve can only called on the root context"),this.resolvedRefs[n]||(this.resolvedRefs[n]=[]),this.resolvedRefs[n].push({modelSchema:e,value:r}),n in this.pendingRefs)for(var i=this.pendingRefs[n].length-1;i>=0;i--){var o=this.pendingRefs[n][i];s(e,o.modelSchema)&&(this.pendingRefs[n].splice(i,1),this.pendingRefsCount--,o.callback(null,r))}},e.prototype.setTarget=function(e){this.isRoot&&this.target&&A["delete"](this.target),this.target=e,A.set(this.target,this)},e.prototype.cancelAwaits=function(){t(this.isRoot,"cancelAwaits can only be called on the root context");var e=this;Object.keys(this.pendingRefs).forEach((function(n){e.pendingRefs[n].forEach((function(t){e.pendingRefsCount--,t.callback(Error("Reference resolution canceled for "+n))}))})),this.pendingRefs={},this.pendingRefsCount=0},e}();function E(e,n,i,o,u){if(null!==i&&i!==undefined&&"object"==typeof i){var f=new k(e,n,i,o,u),a=n.factory(f);t(!!a,"No object returned from factory"),f.setTarget(a);var c=f.createCallback(r);return N(f,n,i,a),c(),a}o(null,null)}function N(e,n,r,i){var u;n["extends"]&&N(e,n["extends"],r,i);for(var f=function(f){var a=n.props[f];if(!a)return{value:void 0};if("*"===f)return function(e,n,r,i,u){var f=function(f){if(!(f in n.props||function(e,n){for(var t in e.props){var r=e.props[t];if("object"==typeof r&&r.jsonname===n)return!0}return!1}(n,f))){var a=u[f];!0===r?(t(o(a),"encountered non primitive value while deserializing '*' properties in property '"+f+"': "+a),i[f]=a):!r||r.pattern&&!r.pattern.test(f)||r.deserializer(a,e.rootContext.createCallback((function(e){return e!==b&&(i[f]=e)})),e)}};for(var a in u)f(a)}(e,n,a,i,r),{value:void 0};!0===a&&(a=j);var c=null!=(u=a.jsonname)?u:f;t("symbol"!=typeof c,"You must alias symbol properties. prop = %l",f);var s=r[c],l=a;D((function(n,t){n||t===undefined||function(n,t,o){var u=e.rootContext.createCallback((function(e){return e!==b&&(i[o]=e)}));n.deserializer(t,(function(i,f){return I(u,i,f,t,r,o,e,n)}),e,i[o])}(l,t,f)}),s,r,c,e,a)},a=0,c=Object.keys(n.props);a<c.length;a++){var s=f(c[a]);if("object"==typeof s)return s.value}}var D=function(e,n,t,r,i,o){o&&"function"==typeof o.beforeDeserialize?o.beforeDeserialize(e,n,t,r,i,o):e(null,n)},I=function(e,n,t,r,i,o,u,f){f&&"function"==typeof f.afterDeserialize?f.afterDeserialize(e,n,t,r,i,o,u,f):e(n,t)};function M(e,n){return t("object"==typeof e||"function"==typeof e,"No modelschema provided. If you are importing it from another file be aware of circular dependencies."),d({serializer:function(n){return t(u(e=p(e)),"expected modelSchema, got "+e),null===n||n===undefined?n:O(e,n)},deserializer:function(n,r,i){t(u(e=p(e)),"expected modelSchema, got "+e),null!==n&&n!==undefined?E(i,e,n,r,undefined):r(null,n)}},n)}function J(e,n){return t(f(e=e||j),"expected prop schema as first argument"),t(!a(e),"provided prop is aliased, please put aliases first"),d({serializer:function(n){return n===undefined?b:(t(n&&"length"in n&&"map"in n,"expected array (like) object"),n.map(e.serializer))},deserializer:function(n,t,r){Array.isArray(n)?i(n,(function(t,i,o){function u(u,f){"function"==typeof e.afterDeserialize?I(i,u,f,t,n,o,r,e):i(u,f)}D((function(n,t){n?i(n):e.deserializer(t,u,r)}),t,n,o,r,e)}),t):t("[serializr] expected JSON array")}},n)}e.SKIP=b,e.alias=function(e,n){return t(e&&"string"==typeof e,"expected prop name as first argument"),t(f(n=n&&!0!==n?n:j),"expected prop schema as second argument"),t(!a(n),"provided prop is already aliased"),{jsonname:e,serializer:n.serializer,deserializer:n.deserializer,identifier:c(n)||undefined,beforeDeserialize:n.beforeDeserialize,afterDeserialize:n.afterDeserialize}},e.cancelDeserialize=function(e){t("object"==typeof e&&e&&!Array.isArray(e),"cancelDeserialize needs an object");var n,r=(n=e,A.get(n));r&&r.cancelAwaits()},e.createModelSchema=h,e.createSimpleSchema=function(e){return{factory:function(){return{}},props:e}},e.custom=function(e,n,r){return t("function"==typeof e,"first argument should be function"),t("function"==typeof n,"second argument should be a function or promise"),d({serializer:e,deserializer:function(e,t,r,i){var o=n(e,r,i,t);4!==n.length&&t(null,o)}},r)},e.date=function(e){return d({serializer:function(e){return null===e||e===undefined?e:(t(e instanceof Date,"Expected Date object"),e.getTime())},deserializer:function(e,n){null!==e&&e!==undefined?n(null,new Date(e)):n(null,e)}},e)},e.deserialize=function(e,n,o,f){void 0===o&&(o=r),t(arguments.length>=2,"deserialize expects at least 2 arguments");var a=p(e);if(t(u(a),"first argument should be model schema"),Array.isArray(n)){var c=[];return i(n,(function(e,n){var t=E(undefined,a,e,n,f);c.push(t)}),o),c}return E(undefined,a,n,o,f)},e.getDefaultModelSchema=p,e.identifier=function(e,n){var r,i;return"function"==typeof e?(r=e,i=n):i=e,t(!i||"object"==typeof i,"Additional property arguments should be an object, register function should be omitted or a funtion"),d({identifier:!0,serializer:j.serializer,deserializer:function(e,n,t){j.deserializer(e,(function(e,i){!function(e,n,t){t.rootContext.resolve(t.modelSchema,e,t.target)}(i,t.target,t),r&&r(i,t.target,t),n(e,i)}),t)}},i)},e.list=J,e.map=function(e,n){return t(f(e=e||j),"expected prop schema as first argument"),t(!a(e),"provided prop is aliased, please put aliases first"),d({serializer:function(n){t(n&&"object"==typeof n,"expected object or Map");var r={};if(l(n))n.forEach((function(t,i){return r[i]=e.serializer(t,i,n)}));else for(var i in n)r[i]=e.serializer(n[i],i,n);return r},deserializer:function(t,r,i,o){if(t&&"object"==typeof t){var u=Object.keys(t);J(e,n).deserializer(u.map((function(e){return t[e]})),(function(e,n){if(e)r(e);else{var t,i=l(o);i?(o.clear(),t=o):t={};for(var f=0,a=u.length;f<a;f++)i?t.set(u[f],n[f]):t[u[f]]=n[f];r(null,t)}}),i)}else r("[serializr] expected JSON object")}},n)},e.mapAsArray=function(e,n,r){return t(f(e=e||j),"expected prop schema as first argument"),t(!!n,"expected key property name as second argument"),d({serializer:function(n){t(n&&"object"==typeof n,"expected object or Map");var r=[];if(l(n))n.forEach((function(t,i){return r.push(e.serializer(t,i,n))}));else for(var i in n)r.push(e.serializer(n[i],i,n));return r},deserializer:function(t,i,o,u){J(e,r).deserializer(t,(function(e,r){if(e)i(e);else{var o,f=l(u);f?(u.clear(),o=u):o={};for(var a=0,c=t.length;a<c;a++)f?o.set(r[a][n],r[a]):o[r[a][n].toString()]=r[a];i(null,o)}}),o,undefined)}},r)},e.object=M,e.optional=function(e){t(f(e=e&&!0!==e?e:j),"expected prop schema as second argument");var n=e.serializer;return t("function"==typeof n,"expected prop schema to have a callable serializer"),Object.assign({},e,{serializer:function(e,t,r){var i=n(e,t,r);return i===undefined?b:i}})},e.primitive=y,e.raw=function(e){return d({serializer:function(e){return e},deserializer:function(e,n){n(null,e)}},e)},e.reference=function(e,n,r){t(!!e,"No modelSchema provided. If you are importing it from another file be aware of circular dependencies.");var i="function"==typeof n?n:undefined;r=r||(i?undefined:n);var o,f=!1;function a(){if(f=!0,t("string"!=typeof e||"function"==typeof i,"if the reference target is specified by attribute name, a lookup function is required"),t(!i||"function"==typeof i,"second argument should be a lookup function or additional arguments object"),"string"==typeof e)o=e;else{var n=p(e);t(u(n),"expected model schema or string as first argument for 'ref', got "+n),i=i||function(e){return function(n,t,r){r.rootContext.await(e,n,t)}}(n),o=function(e){t(u(e),"modelSchema must be a ModelSchema");for(var n=e;n;){for(var r in n.props)if(c(n.props[r]))return r;n=n["extends"]}return undefined}(n),t(!!o,"provided model schema doesn't define an identifier() property and cannot be used by 'ref'.")}}return d({serializer:function(e){return f||a(),e?e[o]:null},deserializer:function(e,n,t){f||a(),null===e||e===undefined?n(null,e):i(e,n,t)}},r)},e.serializable=function(e,n,r){if(!n){var i=!0===e?j:e;return t(f(i),"@serializable expects prop schema"),S.bind(null,i)}S(y(),e,n,r)},e.serialize=O,e.serializeAll=function(e,n){var r;if(1===arguments.length)return r=!0,i(e);function i(e){t("function"==typeof e,"@serializeAll can only be used as class decorator");var n=p(e);return n||v(e,n=h(e,{})),n.props["*"]=r,e}return t("object"==typeof e&&e.test,"@serializeAll pattern doesn't have test"),"function"==typeof n&&(n=M(n)),!0===n&&(n=j),t(f(n),"couldn't resolve schema"),r=Object.assign({},n,{pattern:e}),i},e.setDefaultModelSchema=v,e.update=function(e,n,i,o,f){var a=2===arguments.length||"function"==typeof arguments[2];a?(e=p(n=arguments[0]),i=arguments[1],o=arguments[2],f=arguments[3]):e=p(e),t(u(e),"update failed to determine schema"),t("object"==typeof n&&n&&!Array.isArray(n),"update needs an object");var c=new k(undefined,e,i,o||r,f);c.setTarget(n);var s=c.createCallback(r),l=N(c,e,i,n);return s(),l},Object.defineProperty(e,"t",{value:!0})},"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e=e||self).serializr={}); | ||
//# sourceMappingURL=serializr.min.js.map |
141
package.json
{ | ||
"name": "serializr", | ||
"version": "1.5.4", | ||
"description": "Serialize and deserialize complex object graphs to JSON", | ||
"main": "lib/serializr.js", | ||
"module": "lib/es/serializr.js", | ||
"typings": "serializr.d.ts", | ||
"scripts": { | ||
"test": "npm run build-test && node test/index", | ||
"lint": "eslint src", | ||
"prepublish": "npm run build && npm run build-docs", | ||
"build": "node scripts/build.js", | ||
"build-docs": "documentation readme src/serializr.js --github --section API", | ||
"build-test": "npm run build-test-babel && npm run build-test-ts", | ||
"build-test-ts": "tsc -p test/typescript", | ||
"build-test-babel": "babel test/babel/babel.js -o test/babel/babel-compiled.js", | ||
"coverage": "npm run build-test && istanbul cover tape test/*.js" | ||
}, | ||
"keywords": [ | ||
"serialize", | ||
"deserialize", | ||
"graph", | ||
"json", | ||
"mobx" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/mobxjs/serializr.git" | ||
}, | ||
"author": "Michel Weststrate", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/mobxjs/serializr/issues" | ||
}, | ||
"files": [ | ||
"lib", | ||
"serializr.d.ts" | ||
], | ||
"devDependencies": { | ||
"@babel/cli": "^7.2.3", | ||
"@babel/plugin-proposal-class-properties": "^7.3.0", | ||
"@babel/plugin-proposal-decorators": "^7.3.0", | ||
"@babel/preset-env": "^7.3.1", | ||
"babel-eslint": "^10.0.1", | ||
"coveralls": "^3.0.2", | ||
"documentation": "^9.1.1", | ||
"eslint": "^5.13.0", | ||
"eslint-plugin-import": "^2.16.0", | ||
"istanbul": "^0.4.4", | ||
"lodash.merge": "^4.6.0", | ||
"mobx": "^2.4.1 || ^3.0.0 || ^4.0.0 || ^5.0.0", | ||
"rollup": "^1.1.2", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
"rollup-plugin-terser": "^4.0.4", | ||
"tape": "^4.9.2", | ||
"typescript": "^3.3.3" | ||
} | ||
} | ||
"name": "serializr", | ||
"version": "2.0.0-beta1", | ||
"description": "Serialize and deserialize complex object graphs to JSON", | ||
"main": "lib/serializr.js", | ||
"module": "lib/es/serializr.js", | ||
"typings": "lib/serializr.d.ts", | ||
"scripts": { | ||
"test": "nyc --reporter lcov tape test/*.js", | ||
"lint": "eslint src", | ||
"prepare": "yarn run build && yarn run build-docs", | ||
"build": "rollup --config", | ||
"build-docs": "ts-node --transpile-only --project gendoc.tsconfig.json ./gendoc.tsx", | ||
"build-test": "yarn run build-test-babel && yarn run build-test-ts", | ||
"build-test-ts": "tsc -p test/typescript", | ||
"build-test-babel": "babel test/babel/babel.js -o test/babel/babel-compiled.js", | ||
"prettier": "prettier --write {src,test}/**/*.{ts,js}", | ||
"clean": "rimraf lib test/typescript/ts.js test/babel/babel-compiled.js", | ||
"watch-docs": "nodemon -w gendoc.tsx -w src -e ts,tsx,md -x ts-node --transpile-only --project gendoc.tsconfig.json ./gendoc.tsx" | ||
}, | ||
"nyc": { | ||
"all": true, | ||
"include": [ | ||
"src/**/*", | ||
"lib/**/*" | ||
] | ||
}, | ||
"keywords": [ | ||
"serialize", | ||
"deserialize", | ||
"graph", | ||
"json", | ||
"mobx" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/mobxjs/serializr.git" | ||
}, | ||
"author": "Michel Weststrate", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/mobxjs/serializr/issues" | ||
}, | ||
"files": [ | ||
"lib", | ||
"serializr.d.ts" | ||
], | ||
"devDependencies": { | ||
"@babel/cli": "^7.2.3", | ||
"@babel/plugin-proposal-class-properties": "^7.3.0", | ||
"@babel/plugin-proposal-decorators": "^7.3.0", | ||
"@babel/preset-env": "^7.3.1", | ||
"@rollup/plugin-commonjs": "^11.0.2", | ||
"@rollup/plugin-node-resolve": "^7.1.0", | ||
"@types/react": "^16.9.19", | ||
"@types/react-dom": "^16.9.5", | ||
"@types/tape": "^4.2.33", | ||
"babel-eslint": "^10.0.1", | ||
"coveralls": "^3.0.2", | ||
"documentation": "^12.1.4", | ||
"eslint": "^5.13.0", | ||
"eslint-plugin-import": "^2.16.0", | ||
"lodash.merge": "^4.6.0", | ||
"mobx": "^2.4.1 || ^3.0.0 || ^4.0.0 || ^5.0.0", | ||
"nodemon": "^2.0.2", | ||
"nyc": "^15.0.0", | ||
"prettier": "^1.19.1", | ||
"react": "^16.12.0", | ||
"react-dom": "^16.12.0", | ||
"react-markdown": "^4.3.1", | ||
"rimraf": "^3.0.1", | ||
"rollup": "^1.1.2", | ||
"rollup-plugin-terser": "^5.2.0", | ||
"rollup-plugin-typescript2": "^0.25.3", | ||
"tape": "^4.9.2", | ||
"ts-node": "^8.6.2", | ||
"turndown": "^5.0.3", | ||
"turndown-plugin-gfm": "^1.0.2", | ||
"typedoc": "^0.17.0-3", | ||
"typescript": "^3.3.3", | ||
"unescape": "^1.0.1" | ||
}, | ||
"dependencies": {} | ||
} |
1142
README.md
@@ -8,2 +8,3 @@ # Serializr | ||
[![Join the chat at https://gitter.im/mobxjs/serializr](https://badges.gitter.im/mobxjs/serializr.svg)](https://gitter.im/mobxjs/serializr?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
[![NPM](https://img.shields.io/npm/v/serializr)](https://www.npmjs.com/package/serializr) | ||
@@ -53,15 +54,15 @@ _Serializr is feature complete, and easily extendable. Since there are no active maintainers the project is frozen feature wise. Bug reports and well designed pull requests are welcome and will be addressed._ | ||
serialize, | ||
deserialize, | ||
} from 'serializr'; | ||
deserialize | ||
} from "serializr" | ||
// Example model classes | ||
class User { | ||
uuid = Math.floor(Math.random() * 10000); | ||
displayName = 'John Doe'; | ||
uuid = Math.floor(Math.random() * 10000) | ||
displayName = "John Doe" | ||
} | ||
class Message { | ||
message = 'Test'; | ||
author = null; | ||
comments = []; | ||
message = "Test" | ||
author = null | ||
comments = [] | ||
} | ||
@@ -72,6 +73,6 @@ | ||
// In a real app this might be a database query | ||
const user = new User(); | ||
user.uuid = uuid; | ||
user.displayName = `John Doe ${uuid}`; | ||
return user; | ||
const user = new User() | ||
user.uuid = uuid | ||
user.displayName = `John Doe ${uuid}` | ||
return user | ||
} | ||
@@ -84,3 +85,3 @@ | ||
// context is an object detailing the execution context of the serializer now | ||
callback(null, fetchUserSomewhere(uuid)); | ||
callback(null, fetchUserSomewhere(uuid)) | ||
} | ||
@@ -92,25 +93,25 @@ | ||
author: reference(User, findUserById), | ||
comments: list(object(Message)), | ||
}); | ||
comments: list(object(Message)) | ||
}) | ||
createModelSchema(User, { | ||
uuid: identifier(), | ||
displayName: primitive(), | ||
}); | ||
displayName: primitive() | ||
}) | ||
// can now deserialize and serialize! | ||
const message = deserialize(Message, { | ||
message: 'Hello world', | ||
message: "Hello world", | ||
author: 17, | ||
comments: [ | ||
{ | ||
message: 'Welcome!', | ||
author: 23, | ||
}, | ||
], | ||
}); | ||
message: "Welcome!", | ||
author: 23 | ||
} | ||
] | ||
}) | ||
const json = serialize(message); | ||
const json = serialize(message) | ||
console.dir(message, { colors: true, depth: 10 }); | ||
console.dir(message, { colors: true, depth: 10 }) | ||
``` | ||
@@ -133,21 +134,23 @@ | ||
getDefaultModelSchema, | ||
serializable, | ||
} from 'serializr'; | ||
serializable | ||
} from "serializr" | ||
class User { | ||
@serializable(identifier()) | ||
uuid = Math.random(); | ||
uuid = Math.random() | ||
@serializable displayName = 'John Doe'; | ||
@serializable | ||
displayName = "John Doe" | ||
} | ||
class Message { | ||
@serializable message = 'Test'; | ||
@serializable | ||
message = "Test" | ||
@serializable(object(User)) | ||
author = null; | ||
author = null | ||
// Self referencing decorators work in Babel 5.x and Typescript. See below for more. | ||
@serializable(list(object(Message))) | ||
comments = []; | ||
comments = [] | ||
} | ||
@@ -157,13 +160,13 @@ | ||
const message = deserialize(Message, { | ||
message: 'Hello world', | ||
author: { uuid: 1, displayName: 'Alice' }, | ||
message: "Hello world", | ||
author: { uuid: 1, displayName: "Alice" }, | ||
comments: [ | ||
{ | ||
message: 'Welcome!', | ||
author: { uuid: 1, displayName: 'Bob' }, | ||
}, | ||
], | ||
}); | ||
message: "Welcome!", | ||
author: { uuid: 1, displayName: "Bob" } | ||
} | ||
] | ||
}) | ||
console.dir(message, { colors: true, depth: 10 }); | ||
console.dir(message, { colors: true, depth: 10 }) | ||
@@ -173,3 +176,3 @@ // We can call serialize without the first argument here | ||
const json = serialize(message); | ||
const json = serialize(message) | ||
``` | ||
@@ -183,13 +186,11 @@ | ||
class Message { | ||
@serializable message = 'Test'; | ||
@serializable message = "Test" | ||
@serializable(object(User)) | ||
author = null; | ||
author = null | ||
comments = []; | ||
comments = [] | ||
constructor() { | ||
getDefaultModelSchema(Message).props['comments'] = list( | ||
object(Message) | ||
); | ||
getDefaultModelSchema(Message).props["comments"] = list(object(Message)) | ||
} | ||
@@ -213,4 +214,4 @@ } | ||
"plugins": [ | ||
["@babel/plugin-proposal-decorators", { "legacy": true}], | ||
["@babel/plugin-proposal-class-properties", { "loose": true}] | ||
["@babel/plugin-proposal-decorators", { "legacy": true }], | ||
["@babel/plugin-proposal-class-properties", { "loose": true }] | ||
] | ||
@@ -234,5 +235,5 @@ } | ||
```json | ||
{ | ||
"stage": 1 | ||
} | ||
{ | ||
"stage": 1 | ||
} | ||
``` | ||
@@ -259,5 +260,5 @@ | ||
props: { | ||
modelfield: PropSchema, | ||
}, | ||
}; | ||
modelfield: PropSchema | ||
} | ||
} | ||
``` | ||
@@ -331,25 +332,41 @@ | ||
const myHandler = { | ||
beforeDeserialize: function (callback, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
if (typeof jsonValue === 'string') { | ||
callback(null, jsonValue) | ||
} else if (typeof jsonValue === 'number') { | ||
callback(null, jsonValue.toString()) | ||
} else { | ||
callback(new Error('something went wrong before deserialization')) | ||
} | ||
}, | ||
afterDeserialize: function (callback, error, newValue, jsonValue, jsonParentValue, propNameOrIndex, context, | ||
propDef, numRetry) { | ||
if (!error && newValue !== 'needs change') { | ||
callback(null, newValue) | ||
} else if (!error && newValue === 'needs change') { | ||
callback(new Error(), 'changed value') | ||
} else { | ||
callback(error) | ||
} | ||
} | ||
beforeDeserialize: function( | ||
callback, | ||
jsonValue, | ||
jsonParentValue, | ||
propNameOrIndex, | ||
context, | ||
propDef | ||
) { | ||
if (typeof jsonValue === "string") { | ||
callback(null, jsonValue) | ||
} else if (typeof jsonValue === "number") { | ||
callback(null, jsonValue.toString()) | ||
} else { | ||
callback(new Error("something went wrong before deserialization")) | ||
} | ||
}, | ||
afterDeserialize: function( | ||
callback, | ||
error, | ||
newValue, | ||
jsonValue, | ||
jsonParentValue, | ||
propNameOrIndex, | ||
context, | ||
propDef | ||
) { | ||
if (!error && newValue !== "needs change") { | ||
callback(null, newValue) | ||
} else if (!error && newValue === "needs change") { | ||
callback(new Error(), "changed value") | ||
} else { | ||
callback(error) | ||
} | ||
} | ||
} | ||
class MyData { | ||
@serializable(primitive(myHandler)) mySimpleField | ||
@serializable(primitive(myHandler)) | ||
mySimpleField | ||
} | ||
@@ -360,496 +377,251 @@ ``` | ||
# API | ||
<!-- START API AUTOGEN --> | ||
<!-- THIS SECTION WAS AUTOGENERATED BY gendoc.tsx! DO NOT EDIT! --> | ||
API | ||
--- | ||
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> | ||
### _interface_ `ModelSchema`<T><sub><a href="src/api/types.ts#L65">src</a></sub> | ||
### Table of Contents | ||
#### property `targetClass`?: [Clazz](#type-clazzt----src)<any> | ||
- [ModelSchema](#modelschema) | ||
- [Parameters](#parameters) | ||
- [Properties](#properties) | ||
- [createSimpleSchema](#createsimpleschema) | ||
- [Parameters](#parameters-1) | ||
- [Examples](#examples) | ||
- [createModelSchema](#createmodelschema) | ||
- [Parameters](#parameters-2) | ||
- [Examples](#examples-1) | ||
- [getDefaultModelSchema](#getdefaultmodelschema) | ||
- [Parameters](#parameters-3) | ||
- [setDefaultModelSchema](#setdefaultmodelschema) | ||
- [Parameters](#parameters-4) | ||
- [serializable](#serializable) | ||
- [Parameters](#parameters-5) | ||
- [Examples](#examples-2) | ||
- [serialize](#serialize) | ||
- [Parameters](#parameters-6) | ||
- [serializeAll](#serializeall) | ||
- [Parameters](#parameters-7) | ||
- [Examples](#examples-3) | ||
- [cancelDeserialize](#canceldeserialize) | ||
- [Parameters](#parameters-8) | ||
- [deserialize](#deserialize) | ||
- [Parameters](#parameters-9) | ||
- [update](#update) | ||
- [Parameters](#parameters-10) | ||
- [primitive](#primitive) | ||
- [Parameters](#parameters-11) | ||
- [Examples](#examples-4) | ||
- [identifier](#identifier) | ||
- [Parameters](#parameters-12) | ||
- [Examples](#examples-5) | ||
- [date](#date) | ||
- [Parameters](#parameters-13) | ||
- [alias](#alias) | ||
- [Parameters](#parameters-14) | ||
- [Examples](#examples-6) | ||
- [custom](#custom) | ||
- [Parameters](#parameters-15) | ||
- [Examples](#examples-7) | ||
- [object](#object) | ||
- [Parameters](#parameters-16) | ||
- [Examples](#examples-8) | ||
- [optional](#optional) | ||
- [Parameters](#parameters-17) | ||
- [Examples](#examples-9) | ||
- [reference](#reference) | ||
- [Parameters](#parameters-18) | ||
- [Examples](#examples-10) | ||
- [list](#list) | ||
- [Parameters](#parameters-19) | ||
- [Examples](#examples-11) | ||
- [map](#map) | ||
- [Parameters](#parameters-20) | ||
- [mapAsArray](#mapasarray) | ||
- [Parameters](#parameters-21) | ||
- [raw](#raw) | ||
- [Parameters](#parameters-22) | ||
- [Examples](#examples-12) | ||
- [SKIP](#skip) | ||
- [Examples](#examples-13) | ||
#### property `factory`: (_context_: [Context](typedoc-id-undefined)) => T | ||
## ModelSchema | ||
#### property `props`: [Props](#type-propst----src)<T> | ||
[src/serializr.js:52-52](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/serializr.js#L5-L51 "Source code on GitHub") | ||
#### property `extends`?: [ModelSchema](#interface-modelschematsrc)<any> | ||
JSDOC type defintions for usage w/o typescript. | ||
### _interface_ `PropSchema`<sub><a href="src/api/types.ts#L22">src</a></sub> | ||
Type: [object](#object) | ||
#### property `serializer`: [PropSerializer](#type-propserializer--sourcepropertyvalue-any-key-string--number--symbol-sourceobject-any--any--typeof-skip-src) | ||
### Parameters | ||
#### property `deserializer`: [PropDeserializer](#type-propdeserializer--jsonvalue-any-callback-err-any-targetpropertyvalue-any--typeof-skip--void-context-context-currentpropertyvalue-any--void-src) | ||
- `value` **any** | ||
- `writeable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `get` **([Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function) \| [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** | ||
- `set` **([Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function) \| [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** | ||
- `configurable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `enumerable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `sourcePropertyValue` **any** | ||
- `jsonValue` **any** | ||
- `callback` **cpsCallback** | ||
- `context` **Context** | ||
- `currentPropertyValue` **any** | ||
- `id` **any** | ||
- `target` **[object](#object)** | ||
- `context` **Context** | ||
- `result` **any** | ||
- `error` **any** | ||
- `id` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** | ||
- `callback` **cpsCallback** | ||
- `factory` | ||
- `props` | ||
- `targetClass` | ||
#### property `beforeDeserialize`?: [BeforeDeserializeFunc](#type-beforedeserializefunc--callback-err-any-value-any--void-jsonvalue-any-jsonparentvalue-any-propnameorindex-string--number-context-context-propdef-propschema--void-src) | ||
### Properties | ||
#### property `afterDeserialize`?: [AfterDeserializeFunc](#type-afterdeserializefunc--callback-err-any-value-any--void-err-any-newvalue-any-jsonvalue-any-jsonparentvalue-any-propnameorindex-string--number--symbol-context-context-propdef-propschema--void-src) | ||
- `serializer` **serializerFunction** | ||
- `deserializer` **deserializerFunction** | ||
- `identifier` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
#### property `pattern`?: undefined | { test: (_propName_: string) => boolean } | ||
Returns **any** any - serialized object | ||
Filter properties to which this schema applies. Used with `ModelSchema.props["*"]`. | ||
Returns **any** void | ||
#### property `jsonname`?: undefined | string | ||
Returns **any** void | ||
#### property `identifier`?: undefined | true | ||
Returns **any** void | ||
#### property `paramNumber`?: undefined | number | ||
## createSimpleSchema | ||
### _type_ `AdditionalPropArgs` = [Pick](typedoc-id-undefined)<[PropSchema](#interface-propschemasrc), `"beforeDeserialize"` | `"afterDeserialize"` | `"pattern"`> <sub><a href="src/api/types.ts#L7">src</a></sub> | ||
[src/api/createSimpleSchema.js:17-24](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/api/createSimpleSchema.js#L17-L24 "Source code on GitHub") | ||
Can be passed to function which create `PropSchema`s to set additional properties. | ||
Creates a model schema that (de)serializes from / to plain javascript objects. | ||
Its factory method is: `() => ({})` | ||
### _type_ `AfterDeserializeFunc` = (_callback_: (_err_: any, _value_: any) => void, _err_: any, _newValue_: any, _jsonValue_: any, _jsonParentValue_: any, _propNameOrIndex_: string | number | symbol, _context_: [Context](typedoc-id-undefined), _propDef_: [PropSchema](#interface-propschemasrc)) => void <sub><a href="src/api/types.ts#L36">src</a></sub> | ||
### Parameters | ||
### _type_ `BeforeDeserializeFunc` = (_callback_: (_err_: any, _value_: any) => void, _jsonValue_: any, _jsonParentValue_: any, _propNameOrIndex_: string | number, _context_: [Context](typedoc-id-undefined), _propDef_: [PropSchema](#interface-propschemasrc)) => void <sub><a href="src/api/types.ts#L47">src</a></sub> | ||
- `props` **[object](#object)** property mapping, | ||
### _type_ `Clazz`<T> = { } <sub><a href="src/api/types.ts#L72">src</a></sub> | ||
### Examples | ||
### _type_ `ClazzOrModelSchema`<T> = [ModelSchema](#interface-modelschematsrc)<T> | [Clazz](#type-clazzt----src)<T> <sub><a href="src/api/types.ts#L73">src</a></sub> | ||
```javascript | ||
var todoSchema = createSimpleSchema({ | ||
title: true, | ||
done: true, | ||
}); | ||
### _type_ `PropDef` = [PropSchema](#interface-propschemasrc) | boolean | undefined <sub><a href="src/api/types.ts#L63">src</a></sub> | ||
var json = serialize(todoSchema, { title: 'Test', done: false }); | ||
var todo = deserialize(todoSchema, json); | ||
``` | ||
### _type_ `PropDeserializer` = (_jsonValue_: any, _callback_: (_err_?: any, _targetPropertyValue_?: any | typeof [SKIP](typedoc-id-undefined)) => void, _context_: [Context](typedoc-id-undefined), _currentPropertyValue_?: any) => void <sub><a href="src/api/types.ts#L16">src</a></sub> | ||
Returns **[object](#object)** model schema | ||
### _type_ `PropSerializer` = (_sourcePropertyValue_: any, _key_: string | number | symbol, _sourceObject_: any) => any | typeof [SKIP](typedoc-id-undefined) <sub><a href="src/api/types.ts#L11">src</a></sub> | ||
## createModelSchema | ||
### _type_ `Props`<T> = { } <sub><a href="src/api/types.ts#L60">src</a></sub> | ||
[src/api/createModelSchema.js:29-47](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/api/createModelSchema.js#L29-L47 "Source code on GitHub") | ||
true is shorthand for `primitive().` false/undefined will be ignored | ||
Creates a model schema that (de)serializes an object created by a constructor function (class). | ||
The created model schema is associated by the targeted type as default model schema, see setDefaultModelSchema. | ||
Its factory method is `() => new clazz()` (unless overriden, see third arg). | ||
### _type_ `RefLookupFunction` = (_id_: string, _callback_: (_err_: any, _result_: any) => void, _context_: [Context](typedoc-id-undefined)) => void <sub><a href="src/api/types.ts#L75">src</a></sub> | ||
### Parameters | ||
### _type_ `RegisterFunction` = (_id_: any, _object_: any, _context_: [Context](typedoc-id-undefined)) => void <sub><a href="src/api/types.ts#L80">src</a></sub> | ||
- `clazz` **([constructor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
- `props` **[object](#object)** property mapping | ||
- `factory` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** optional custom factory. Receives context as first arg | ||
### _const_ `SKIP`<sub><a href="src/constants.ts#L36">src</a></sub> | ||
### Examples | ||
If you want to skip serialization or deserialization, you can use SKIP. | ||
```javascript | ||
function Todo(title, done) { | ||
this.title = title; | ||
this.done = done; | ||
} | ||
createModelSchema(Todo, { | ||
title: true, | ||
done: true, | ||
}); | ||
var json = serialize(new Todo('Test', false)); | ||
var todo = deserialize(Todo, json); | ||
```ts | ||
const schema = createSimpleSchema({ | ||
a: custom( | ||
() => SKIP, | ||
v => v, | ||
), | ||
}) | ||
serialize(s, { a: 4 }) // {} | ||
deserialize(s, { "a": 4 }) // { a: 4 } | ||
``` | ||
Returns **[object](#object)** model schema | ||
```ts | ||
// Skipping deserialization with computed mobx property. | ||
## getDefaultModelSchema | ||
class TodoState { | ||
// Todo.category is @serializable(reference(...)) | ||
@serializable(list(object(Todo))) | ||
@observable | ||
todos: Todo[] | ||
[src/api/getDefaultModelSchema.js:9-18](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/api/getDefaultModelSchema.js#L9-L18 "Source code on GitHub") | ||
Returns the standard model schema associated with a class / constructor function | ||
### Parameters | ||
- `thing` **[object](#object)** | ||
Returns **[ModelSchema](#modelschema)** model schema | ||
## setDefaultModelSchema | ||
[src/api/setDefaultModelSchema.js:15-18](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/api/setDefaultModelSchema.js#L15-L18 "Source code on GitHub") | ||
Sets the default model schema for class / constructor function. | ||
Everywhere where a model schema is required as argument, this class / constructor function | ||
can be passed in as well (for example when using `object` or `ref`. | ||
When passing an instance of this class to `serialize`, it is not required to pass the model schema | ||
as first argument anymore, because the default schema will be inferred from the instance type. | ||
### Parameters | ||
- `clazz` **([constructor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
- `modelSchema` **[ModelSchema](#modelschema)** a model schema | ||
Returns **[ModelSchema](#modelschema)** model schema | ||
## serializable | ||
[src/api/serializable.js:93-103](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/api/serializable.js#L93-L103 "Source code on GitHub") | ||
Decorator that defines a new property mapping on the default model schema for the class | ||
it is used in. | ||
When using typescript, the decorator can also be used on fields declared as constructor arguments (using the `private` / `protected` / `public` keywords). | ||
The default factory will then invoke the constructor with the correct arguments as well. | ||
### Parameters | ||
- `arg1` | ||
- `arg2` | ||
- `arg3` | ||
### Examples | ||
```javascript | ||
class Todo { | ||
// we want to serialize the categories, so that the references in | ||
// this.todos can be resolved, but we don't want to set this property | ||
@serializable( | ||
list(object(TodoCategory), | ||
{ afterDeserialize: callback => callback(undefined, SKIP) })) | ||
@computed | ||
get categories() { | ||
return this.todos.map(todo => todo.category) | ||
} | ||
} | ||
``` | ||
Returns **PropertyDescriptor** | ||
### _function_ `alias`(_name_: string, _propSchema_?: [PropDef](#type-propdef--propschema--boolean--undefined-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/alias.ts#L24">src</a></sub> | ||
## serialize | ||
Alias indicates that this model property should be named differently in the generated json. Alias should be the outermost propschema. | ||
[src/core/serialize.js:14-36](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/core/serialize.js#L14-L36 "Source code on GitHub") | ||
```ts | ||
createModelSchema(Todo, { | ||
title: alias('task', primitive()), | ||
}) | ||
Serializes an object (graph) into json using the provided model schema. | ||
The model schema can be omitted if the object type has a default model schema associated with it. | ||
If a list of objects is provided, they should have an uniform type. | ||
### Parameters | ||
- `arg1` class or modelschema to use. Optional | ||
- `arg2` object(s) to serialize | ||
Returns **[object](#object)** serialized representation of the object | ||
## serializeAll | ||
[src/core/serializeAll.js:38-67](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/core/serializeAll.js#L38-L67 "Source code on GitHub") | ||
The `serializeAll` decorator can may used on a class to signal that all primitive properties, | ||
or complex properties with a name matching a `pattern`, should be serialized automatically. | ||
### Parameters | ||
- `targetOrPattern` | ||
- `clazzOrSchema` | ||
### Examples | ||
```javascript | ||
class DataType { | ||
serialize(new Todo('test')) // { "task": "test" } | ||
``` | ||
## cancelDeserialize | ||
### _function_ `cancelDeserialize`<T>(_instance_: T): void <sub><a href="src/core/cancelDeserialize.ts#L11">src</a></sub> | ||
[src/core/cancelDeserialize.js:12-18](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/core/cancelDeserialize.js#L12-L18 "Source code on GitHub") | ||
Cancels an asynchronous deserialization or update operation for the specified target object. | ||
### Parameters | ||
### _function_ `createModelSchema`<T>(_clazz_: [Clazz](#type-clazzt----src)<T>, _props_: [Props](#type-propst----src), _factory_?: undefined | ((_context_: [Context](typedoc-id-undefined)) => T)): [ModelSchema](#interface-modelschematsrc)<T> <sub><a href="src/api/createModelSchema.ts#L31">src</a></sub> | ||
- `instance` object that was previously returned from deserialize or update method | ||
Creates a model schema that (de)serializes an object created by a constructor function (class). The created model schema is associated by the targeted type as default model schema, see setDefaultModelSchema. Its factory method is `() => new clazz()` (unless overriden, see third arg). | ||
## deserialize | ||
```ts | ||
function Todo(title, done) { | ||
this.title = title | ||
this.done = done | ||
} | ||
[src/core/deserialize.js:69-87](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/core/deserialize.js#L69-L87 "Source code on GitHub") | ||
Deserializes a json structure into an object graph. | ||
This process might be asynchronous (for example if there are references with an asynchronous | ||
lookup function). The function returns an object (or array of objects), but the returned object | ||
might be incomplete until the callback has fired as well (which might happen immediately) | ||
### Parameters | ||
- `schema` **([object](#object) \| [array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array))** to use for deserialization | ||
- `json` **[json](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)** data to deserialize | ||
- `callback` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** node style callback that is invoked once the deserialization has | ||
finished. First argument is the optional error, second argument is the deserialized object | ||
(same as the return value) | ||
- `customArgs` **any** custom arguments that are available as `context.args` during the | ||
deserialization process. This can be used as dependency injection mechanism to pass in, for | ||
example, stores. | ||
Returns **([object](#object) \| [array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array))** deserialized object, possibly incomplete. | ||
## update | ||
[src/core/update.js:22-44](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/core/update.js#L22-L44 "Source code on GitHub") | ||
Similar to deserialize, but updates an existing object instance. | ||
Properties will always updated entirely, but properties not present in the json will be kept as is. | ||
Further this method behaves similar to deserialize. | ||
### Parameters | ||
- `modelSchema` **[object](#object)** , optional if it can be inferred from the instance type | ||
- `target` **[object](#object)** target instance to update | ||
- `json` **[object](#object)** the json to deserialize | ||
- `callback` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** the callback to invoke once deserialization has completed. | ||
- `customArgs` **any** custom arguments that are available as `context.args` during the deserialization process. This can be used as dependency injection mechanism to pass in, for example, stores. | ||
Returns **([object](#object) \| [array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array))** deserialized object, possibly incomplete. | ||
## primitive | ||
[src/types/primitive.js:18-32](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/primitive.js#L18-L32 "Source code on GitHub") | ||
Indicates that this field contains a primitive value (or Date) which should be serialized literally to json. | ||
### Parameters | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
### Examples | ||
```javascript | ||
createModelSchema(Todo, { | ||
title: primitive(), | ||
}); | ||
title: true, | ||
done: true, | ||
}) | ||
console.dir(serialize(new Todo('test'))); | ||
// outputs: { title : "test" } | ||
const json = serialize(new Todo('Test', false)) | ||
const todo = deserialize(Todo, json) | ||
``` | ||
Returns **[ModelSchema](#modelschema)** | ||
### _function_ `createSimpleSchema`<T>(_props_: [Props](#type-propst----src)): [ModelSchema](#interface-modelschematsrc)<T> <sub><a href="src/api/createSimpleSchema.ts#L19">src</a></sub> | ||
## identifier | ||
Creates a model schema that (de)serializes from / to plain javascript objects. Its factory method is: `() => ({})` | ||
[src/types/identifier.js:43-66](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/identifier.js#L43-L66 "Source code on GitHub") | ||
Similar to primitive, but this field will be marked as the identifier for the given Model type. | ||
This is used by for example `reference()` to serialize the reference | ||
Identifier accepts an optional `registerFn` with the signature: | ||
`(id, target, context) => void` | ||
that can be used to register this object in some store. note that not all fields of this object might | ||
have been deserialized yet. | ||
### Parameters | ||
- `arg1` **(RegisterFunction | AdditionalPropArgs)** optional registerFn: function to register this object during creation. | ||
- `arg2` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
### Examples | ||
```javascript | ||
var todos = {}; | ||
var s = _.createSimpleSchema({ | ||
id: _.identifier((id, object) => (todos[id] = object)), | ||
```ts | ||
const todoSchema = createSimpleSchema({ | ||
title: true, | ||
}); | ||
done: true, | ||
}) | ||
_.deserialize(s, { | ||
id: 1, | ||
title: 'test0', | ||
}); | ||
_.deserialize(s, [{ id: 2, title: 'test2' }, { id: 1, title: 'test1' }]); | ||
t.deepEqual(todos, { | ||
1: { id: 1, title: 'test1' }, | ||
2: { id: 2, title: 'test2' }, | ||
}); | ||
const json = serialize(todoSchema, { title: 'Test', done: false }) | ||
const todo = deserialize(todoSchema, json) | ||
``` | ||
Returns **PropSchema** | ||
### _function_ `custom`(_serializer_: [PropSerializer](#type-propserializer--sourcepropertyvalue-any-key-string--number--symbol-sourceobject-any--any--typeof-skip-src), _deserializer_: (_jsonValue_: any, _context_: any, _oldValue_: any) => any | typeof [SKIP](typedoc-id-undefined), _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/custom.ts#L63">src</a></sub> | ||
## date | ||
Can be used to create simple custom propSchema. Multiple things can be done inside of a custom propSchema, like deserializing and serializing other (polymorphic) objects, skipping the serialization of something or checking the context of the obj being (de)serialized. | ||
[src/types/date.js:9-26](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/date.js#L9-L26 "Source code on GitHub") | ||
The `custom` function takes two parameters, the `serializer` function and the `deserializer` function. | ||
Similar to primitive, serializes instances of Date objects | ||
The `serializer` function has the signature: `(value, key, obj) => void` | ||
### Parameters | ||
When serializing the object `{a: 1}` the `serializer` function will be called with `serializer(1, 'a', {a: 1})`. | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
The `deserializer` function has the following signature for synchronous processing `(value, context, oldValue) => void` | ||
Returns **PropSchema** | ||
For asynchronous processing the function expects the following signature `(value, context, oldValue, callback) => void` | ||
## alias | ||
When deserializing the object `{b: 2}` the `deserializer` function will be called with `deserializer(2, contextObj)` ([contextObj reference](https://github.com/mobxjs/serializr#deserialization-context)). | ||
[src/types/alias.js:20-33](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/alias.js#L20-L33 "Source code on GitHub") | ||
```ts | ||
const schemaDefault = createSimpleSchema({ | ||
a: custom( | ||
v => v + 2, | ||
v => v - 2 | ||
) | ||
}) | ||
serialize(schemaDefault, { a: 4 }) // { "a": 6 } | ||
deserialize(schemaDefault, { "a": 6 }) // { a: 4 } | ||
Alias indicates that this model property should be named differently in the generated json. | ||
Alias should be the outermost propschema. | ||
### Parameters | ||
- `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** name of the json field to be used for this property | ||
- `propSchema` **PropSchema** propSchema to (de)serialize the contents of this field | ||
### Examples | ||
```javascript | ||
createModelSchema(Todo, { | ||
title: alias('task', primitive()), | ||
}); | ||
console.dir(serialize(new Todo('test'))); | ||
// { task : "test" } | ||
const schemaWithAsyncProps = createSimpleSchema({ | ||
a: custom( | ||
v => v + 2, | ||
(v, context, oldValue, callback) => | ||
somePromise(v, context, oldValue) | ||
.then(result => callback(null, result - 2)) | ||
.catch(err => callback(err)) | ||
) | ||
}) | ||
serialize(schemaWithAsyncProps, { a: 4 }) // { "a": 6 } | ||
deserialize(schemaWithAsyncProps, { "a": 6 }, (err, res) => { | ||
res // { a: 4 } | ||
} | ||
``` | ||
Returns **PropSchema** | ||
### _function_ `custom`(_serializer_: [PropSerializer](#type-propserializer--sourcepropertyvalue-any-key-string--number--symbol-sourceobject-any--any--typeof-skip-src), _deserializer_: (_jsonValue_: any, _context_: any, _oldValue_: any, _callback_: (_err_: any, _result_: any | typeof [SKIP](typedoc-id-undefined)) => void) => void, _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/custom.ts#L68">src</a></sub> | ||
## custom | ||
### _function_ `date`(_additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/date.ts#L9">src</a></sub> | ||
[src/types/custom.js:60-75](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/custom.js#L60-L75 "Source code on GitHub") | ||
Similar to primitive, serializes instances of Date objects | ||
Can be used to create simple custom propSchema. Multiple things can be done inside of a custom propSchema, like deserializing and serializing other (polymorphic) objects, skipping the serialization of something or checking the context of the obj being (de)serialized. | ||
### _function_ `deserialize`<T>(_modelschema_: [ClazzOrModelSchema](#type-clazzormodelschemat--modelschemat--clazzt-src)<T>, _jsonArray_: any\[\], _callback_?: undefined | ((_err_: any, _result_: T\[\]) => void), _customArgs_?: any): T\[\] <sub><a href="src/core/deserialize.ts#L76">src</a></sub> | ||
The `custom` function takes two parameters, the `serializer` function and the `deserializer` function. | ||
Deserializes a json structure into an object graph. | ||
The `serializer` function has the signature: | ||
`(value, key, obj) => void` | ||
This process might be asynchronous (for example if there are references with an asynchronous lookup function). The function returns an object (or array of objects), but the returned object might be incomplete until the callback has fired as well (which might happen immediately) | ||
When serializing the object `{a: 1}` the `serializer` function will be called with `serializer(1, 'a', {a: 1})`. | ||
### _function_ `deserialize`<T>(_modelschema_: [ClazzOrModelSchema](#type-clazzormodelschemat--modelschemat--clazzt-src)<T>, _json_: any, _callback_?: undefined | ((_err_: any, _result_: T) => void), _customArgs_?: any): T <sub><a href="src/core/deserialize.ts#L82">src</a></sub> | ||
The `deserializer` function has the following signature for synchronous processing | ||
`(value, context, oldValue) => void` | ||
### _function_ `getDefaultModelSchema`<T>(_thing_: any): [ModelSchema](#interface-modelschematsrc)<T> | undefined <sub><a href="src/api/getDefaultModelSchema.ts#L8">src</a></sub> | ||
For asynchronous processing the function expects the following signature | ||
`(value, context, oldValue, callback) => void` | ||
Returns the standard model schema associated with a class / constructor function | ||
When deserializing the object `{b: 2}` the `deserializer` function will be called with `deserializer(2, contextObj)` ([contextObj reference](https://github.com/mobxjs/serializr#deserialization-context)). | ||
### _function_ `identifier`(_arg1_?: [RegisterFunction](#type-registerfunction--id-any-object-any-context-context--void-src) | [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src), _arg2_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/identifier.ts#L48">src</a></sub> | ||
### Parameters | ||
### _function_ `list`(_propSchema_: [PropSchema](#interface-propschemasrc), _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/list.ts#L42">src</a></sub> | ||
- `serializer` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** function that takes a model value and turns it into a json value | ||
- `deserializer` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** function that takes a json value and turns it into a model value. It also takes context argument, which can allow you to deserialize based on the context of other parameters. | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
List indicates that this property contains a list of things. Accepts a sub model schema to serialize the contents | ||
### Examples | ||
```ts | ||
class SubTask {} | ||
class Task {} | ||
class Todo {} | ||
```javascript | ||
var schemaDefault = _.createSimpleSchema({ | ||
a: _.custom( | ||
function(v) { | ||
return v + 2; | ||
}, | ||
function(v) { | ||
return v - 2; | ||
} | ||
), | ||
}); | ||
t.deepEqual(_.serialize(schemaDefault, { a: 4 }), { a: 6 }); | ||
t.deepEqual(_.deserialize(schemaDefault, { a: 6 }), { a: 4 }); | ||
createModelSchema(SubTask, { | ||
title: true, | ||
}) | ||
createModelSchema(Todo, { | ||
title: true, | ||
subTask: list(object(SubTask)), | ||
}) | ||
var schemaWithAsyncProps = _.createSimpleSchema({ | ||
a: _.customAsync( | ||
function(v) { | ||
return v + 2; | ||
const todo = deserialize(Todo, { | ||
title: 'Task', | ||
subTask: [ | ||
{ | ||
title: 'Sub task 1', | ||
}, | ||
function(v, context, oldValue, callback) { | ||
somePromise(v, context, oldValue).then((result) => { | ||
callback(null, result - 2) | ||
}.catch((err) => { | ||
callback(err) | ||
} | ||
} | ||
), | ||
}); | ||
t.deepEqual(_.serialize(schemaWithAsyncProps, { a: 4 }), { a: 6 }); | ||
_.deserialize(schemaWithAsyncProps, { a: 6 }, (err, res) => { | ||
t.deepEqual(res.a, 4) | ||
}; | ||
], | ||
}) | ||
``` | ||
Returns **PropSchema** | ||
### _function_ `map`(_propSchema_: [PropSchema](#interface-propschemasrc), _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/map.ts#L19">src</a></sub> | ||
## object | ||
Similar to list, but map represents a string keyed dynamic collection. This can be both plain objects (default) or ES6 Map like structures. This will be inferred from the initial value of the targetted attribute. | ||
[src/types/object.js:35-55](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/object.js#L35-L55 "Source code on GitHub") | ||
### _function_ `mapAsArray`(_propSchema_: [PropSchema](#interface-propschemasrc), _keyPropertyName_: string, _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/mapAsArray.ts#L18">src</a></sub> | ||
`object` indicates that this property contains an object that needs to be (de)serialized | ||
using its own model schema. | ||
Similar to map, mapAsArray can be used to serialize a map-like collection where the key is contained in the 'value object'. Example: consider Map<id: number, customer: Customer> where the Customer object has the id stored on itself. mapAsArray stores all values from the map into an array which is serialized. Deserialization returns a ES6 Map or plain object object where the `keyPropertyName` of each object is used for keys. For ES6 maps this has the benefit of being allowed to have non-string keys in the map. The serialized json also may be slightly more compact. | ||
N.B. mind issues with circular dependencies when importing model schema's from other files! The module resolve algorithm might expose classes before `createModelSchema` is executed for the target class. | ||
### _function_ `object`(_modelSchema_: [ClazzOrModelSchema](#type-clazzormodelschemat--modelschemat--clazzt-src)<any>, _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/object.ts#L35">src</a></sub> | ||
### Parameters | ||
`object` indicates that this property contains an object that needs to be (de)serialized using its own model schema. | ||
- `modelSchema` **[ModelSchema](#modelschema)** to be used to (de)serialize the object | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
N.B. mind issues with circular dependencies when importing model schema's from other files! The module resolve algorithm might expose classes before `createModelSchema` is executed for the target class. | ||
### Examples | ||
```javascript | ||
```ts | ||
class SubTask {} | ||
@@ -860,7 +632,7 @@ class Todo {} | ||
title: true, | ||
}); | ||
}) | ||
createModelSchema(Todo, { | ||
title: true, | ||
subTask: object(SubTask), | ||
}); | ||
}) | ||
@@ -872,47 +644,51 @@ const todo = deserialize(Todo, { | ||
}, | ||
}); | ||
}) | ||
``` | ||
Returns **PropSchema** | ||
### _function_ `optional`(_propSchema_?: [PropSchema](#interface-propschemasrc) | boolean): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/optional.ts#L17">src</a></sub> | ||
## optional | ||
Optional indicates that this model property shouldn't be serialized if it isn't present. | ||
[src/types/optional.js:18-31](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/optional.js#L18-L31 "Source code on GitHub") | ||
```ts | ||
createModelSchema(Todo, { | ||
title: optional(primitive()), | ||
}) | ||
Optional indicates that this model property shouldn't be serialized if it isn't present. | ||
serialize(new Todo()) // {} | ||
``` | ||
### Parameters | ||
### _function_ `primitive`(_additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/primitive.ts#L16">src</a></sub> | ||
- `name` | ||
- `propSchema` **PropSchema** propSchema to (de)serialize the contents of this field | ||
Indicates that this field contains a primitive value (or Date) which should be serialized literally to json. | ||
### Examples | ||
```javascript | ||
```ts | ||
createModelSchema(Todo, { | ||
title: optional(primitive()), | ||
}); | ||
title: primitive(), | ||
}) | ||
console.dir(serialize(new Todo())); | ||
// {} | ||
serialize(new Todo('test')) // { "title": "test" } | ||
``` | ||
Returns **PropSchema** | ||
### _function_ `raw`(_additionalArgs_: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/raw.ts#L18">src</a></sub> | ||
## reference | ||
Indicates that this field is only need to putted in the serialized json or deserialized instance, without any transformations. Stay with its original value | ||
[src/types/reference.js:66-105](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/reference.js#L66-L105 "Source code on GitHub") | ||
```ts | ||
createModelSchema(Model, { | ||
rawData: raw(), | ||
}) | ||
serialize(new Model({ rawData: { a: 1, b: [], c: {} } } })) | ||
// { "rawData": { a: 1, b: [], c: {} } } } | ||
``` | ||
### _function_ `reference`(_modelSchema_: [ClazzOrModelSchema](#type-clazzormodelschemat--modelschemat--clazzt-src)<any>, _lookupFn_?: [RefLookupFunction](#type-reflookupfunction--id-string-callback-err-any-result-any--void-context-context--void-src), _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/reference.ts#L82">src</a></sub> | ||
`reference` can be used to (de)serialize references that point to other models. | ||
The first parameter should be either a ModelSchema that has an `identifier()` property (see identifier) | ||
or a string that represents which attribute in the target object represents the identifier of the object. | ||
The first parameter should be either a ModelSchema that has an `identifier()` property (see identifier) or a string that represents which attribute in the target object represents the identifier of the object. | ||
The second parameter is a lookup function that is invoked during deserialization to resolve an identifier to | ||
an object. Its signature should be as follows: | ||
The second parameter is a lookup function that is invoked during deserialization to resolve an identifier to an object. Its signature should be as follows: | ||
`lookupFunction(identifier, callback, context)` where: | ||
1\. `identifier` is the identifier being resolved | ||
2\. `callback` is a node style calblack function to be invoked with the found object (as second arg) or an error (first arg) | ||
3\. `context` see context. | ||
`lookupFunction(identifier, callback, context)` where: 1. `identifier` is the identifier being resolved 2. `callback` is a node style calblack function to be invoked with the found object (as second arg) or an error (first arg) 3. `context` see context. | ||
@@ -923,11 +699,3 @@ The lookupFunction is optional. If it is not provided, it will try to find an object of the expected type and required identifier within the same JSON document | ||
### Parameters | ||
- `target` : ModelSchema or string | ||
- `lookupFn` **(RefLookupFunction | AdditionalPropArgs)** optional function or additionalArgs object | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
### Examples | ||
```javascript | ||
```ts | ||
class User {} | ||
@@ -939,3 +707,3 @@ class Post {} | ||
displayname: primitive(), | ||
}); | ||
}) | ||
@@ -945,3 +713,3 @@ createModelSchema(Post, { | ||
message: primitive(), | ||
}); | ||
}) | ||
@@ -951,5 +719,5 @@ function findUserById(uuid, callback) { | ||
.then(userData => { | ||
deserialize(User, userData, callback); | ||
deserialize(User, userData, callback) | ||
}) | ||
.catch(callback); | ||
.catch(callback) | ||
} | ||
@@ -964,130 +732,89 @@ | ||
(err, post) => { | ||
console.log(post); | ||
console.log(post) | ||
} | ||
); | ||
) | ||
``` | ||
Returns **PropSchema** | ||
### _function_ `reference`(_modelSchema_: [ClazzOrModelSchema](#type-clazzormodelschemat--modelschemat--clazzt-src)<any>, _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/reference.ts#L87">src</a></sub> | ||
## list | ||
### _function_ `reference`(_identifierAttr_: string, _lookupFn_: [RefLookupFunction](#type-reflookupfunction--id-string-callback-err-any-result-any--void-context-context--void-src), _additionalArgs_?: [AdditionalPropArgs](#type-additionalpropargs--pickpropschema-beforedeserialize--afterdeserialize--pattern-src)): [PropSchema](#interface-propschemasrc) <sub><a href="src/types/reference.ts#L91">src</a></sub> | ||
[src/types/list.js:43-105](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/list.js#L43-L105 "Source code on GitHub") | ||
### _function_ `serializable`(_propSchema_: [PropDef](#type-propdef--propschema--boolean--undefined-src)): (_target_: any, _key_: string, _baseDescriptor_?: [PropertyDescriptor](typedoc-id-undefined)) => void <sub><a href="src/api/serializable.ts#L99">src</a></sub> | ||
List indicates that this property contains a list of things. | ||
Accepts a sub model schema to serialize the contents | ||
Decorator that defines a new property mapping on the default model schema for the class it is used in. | ||
### Parameters | ||
When using typescript, the decorator can also be used on fields declared as constructor arguments (using the `private` / `protected` / `public` keywords). The default factory will then invoke the constructor with the correct arguments as well. | ||
- `propSchema` **PropSchema** to be used to (de)serialize the contents of the array | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
```ts | ||
class Todo { | ||
@serializable(primitive()) | ||
title // shorthand for primitves | ||
### Examples | ||
@serializable | ||
done | ||
```javascript | ||
class SubTask {} | ||
class Task {} | ||
class Todo {} | ||
constructor(title, done) { | ||
this.title = title | ||
this.done = done | ||
} | ||
} | ||
createModelSchema(SubTask, { | ||
title: true, | ||
}); | ||
createModelSchema(Todo, { | ||
title: true, | ||
subTask: list(object(SubTask)), | ||
}); | ||
const todo = deserialize(Todo, { | ||
title: 'Task', | ||
subTask: [ | ||
{ | ||
title: 'Sub task 1', | ||
}, | ||
], | ||
}); | ||
const json = serialize(new Todo('Test', false)) | ||
const todo = deserialize(Todo, json) | ||
``` | ||
Returns **PropSchema** | ||
### _function_ `serializable`(_target_: any, _key_: string, _baseDescriptor_?: [PropertyDescriptor](typedoc-id-undefined)): void <sub><a href="src/api/serializable.ts#L102">src</a></sub> | ||
## map | ||
### _function_ `serialize`<T>(_modelSchema_: [ClazzOrModelSchema](#type-clazzormodelschemat--modelschemat--clazzt-src)<T>, _instance_: T): any <sub><a href="src/core/serialize.ts#L15">src</a></sub> | ||
[src/types/map.js:14-65](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/map.js#L14-L65 "Source code on GitHub") | ||
Serializes an object (graph) into json using the provided model schema. The model schema can be omitted if the object type has a default model schema associated with it. If a list of objects is provided, they should have an uniform type. | ||
Similar to list, but map represents a string keyed dynamic collection. | ||
This can be both plain objects (default) or ES6 Map like structures. | ||
This will be inferred from the initial value of the targetted attribute. | ||
### _function_ `serialize`<T>(_instance_: T): any <sub><a href="src/core/serialize.ts#L16">src</a></sub> | ||
### Parameters | ||
### _function_ `serializeAll`<T>(_clazz_: [Clazz](#type-clazzt----src)<T>): [Clazz](#type-clazzt----src)<T> <sub><a href="src/core/serializeAll.ts#L43">src</a></sub> | ||
- `propSchema` **any** | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
The `serializeAll` decorator can used on a class to signal that all primitive properties, or complex properties with a name matching a `pattern`, should be serialized automatically. | ||
Returns **PropSchema** | ||
```ts | ||
@serializeAll | ||
class Store { | ||
a = 3 | ||
b | ||
} | ||
## mapAsArray | ||
const store = new Store() | ||
store.c = 5 | ||
store.d = {} | ||
serialize(store) // { "c": 5 } | ||
``` | ||
[src/types/mapAsArray.js:19-66](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/mapAsArray.js#L19-L66 "Source code on GitHub") | ||
```ts | ||
class DataType { | ||
@serializable | ||
x | ||
@serializable | ||
y | ||
} | ||
Similar to map, mapAsArray can be used to serialize a map-like collection where the key is | ||
contained in the 'value object'. Example: consider Map<id: number, customer: Customer> where the | ||
Customer object has the id stored on itself. mapAsArray stores all values from the map into an | ||
array which is serialized. Deserialization returns a ES6 Map or plain object object where the | ||
`keyPropertyName` of each object is used for keys. For ES6 maps this has the benefit of being | ||
allowed to have non-string keys in the map. The serialized json also may be slightly more | ||
compact. | ||
@serializeAll(/^[a-z]$/, DataType) | ||
class ComplexStore { | ||
} | ||
### Parameters | ||
- `propSchema` **any** | ||
- `keyPropertyName` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** the property of stored objects used as key in the map | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
Returns **PropSchema** | ||
## raw | ||
[src/types/raw.js:18-29](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/types/raw.js#L18-L29 "Source code on GitHub") | ||
Indicates that this field is only need to putted in the serialized json or | ||
deserialized instance, without any transformations. Stay with its original value | ||
### Parameters | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
### Examples | ||
```javascript | ||
createModelSchema(Model, { | ||
rawData: raw(), | ||
}); | ||
console.dir(serialize(new Model({ rawData: { a: 1, b: [], c: {} } } }))); | ||
// outputs: { rawData: { a: 1, b: [], c: {} } } } | ||
const store = new ComplexStore() | ||
store.a = {x: 1, y: 2} | ||
store.b = {} | ||
store.somethingElse = 5 | ||
serialize(store) // { a: {x: 1, y: 2}, b: { x: undefined, y: undefined } } | ||
``` | ||
Returns **[ModelSchema](#modelschema)** | ||
### _function_ `serializeAll`(_pattern_: [RegExp](typedoc-id-undefined), _propertyType_: [PropDef](#type-propdef--propschema--boolean--undefined-src) | [Clazz](#type-clazzt----src)<any>): (_clazz_: [Clazz](#type-clazzt----src)<any>) => [Clazz](#type-clazzt----src)<any> <sub><a href="src/core/serializeAll.ts#L44">src</a></sub> | ||
## SKIP | ||
### _function_ `setDefaultModelSchema`<T>(_clazz_: [Clazz](#type-clazzt----src)<T>, _modelSchema_: [ModelSchema](#interface-modelschematsrc)<T>): [ModelSchema](#interface-modelschematsrc)<T> <sub><a href="src/api/setDefaultModelSchema.ts#L16">src</a></sub> | ||
[src/constants.js:20-20](https://git@github.com/:mobxjs/serializr/blob/caa3000e9ec548fb2d63ea045cc9fdaef38454b2/src/constants.js#L20-L20 "Source code on GitHub") | ||
Sets the default model schema for class / constructor function. Everywhere where a model schema is required as argument, this class / constructor function can be passed in as well (for example when using `object` or `ref`. | ||
In the event that a property needs to be deserialized, but not serialized, you can use the SKIP symbol to omit the property. This has to be used with the custom serializer. | ||
When passing an instance of this class to `serialize`, it is not required to pass the model schema as first argument anymore, because the default schema will be inferred from the instance type. | ||
### Examples | ||
### _function_ `update`(_modelSchema_: any, _target_: any, _json_: any, _callback_: any, _customArgs_?: any): void <sub><a href="src/core/update.ts#L36">src</a></sub><!-- END API AUTOGEN --> | ||
```javascript | ||
var schema = _.createSimpleSchema({ | ||
a: _.custom( | ||
function(v) { | ||
return _.SKIP | ||
}, | ||
function(v) { | ||
return v; | ||
} | ||
), | ||
}); | ||
t.deepEqual(_.serialize(s, { a: 4 }), { }); | ||
t.deepEqual(_.deserialize(s, { a: 4 }), { a: 4 }); | ||
``` | ||
# Recipes and examples | ||
@@ -1102,16 +829,16 @@ | ||
task: primitive(), | ||
owner: reference('_userId', UserStore.findUserById), // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias('children', list(object(todoSchema))), | ||
}, | ||
}; | ||
owner: reference("_userId", UserStore.findUserById), // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias("children", list(object(todoSchema))) | ||
} | ||
} | ||
const todo = deserialize( | ||
todoSchema, | ||
{ task: 'grab coffee', owner: 17, children: [] }, | ||
{ task: "grab coffee", owner: 17, children: [] }, | ||
(err, todo) => { | ||
console.log('finished loading todos'); | ||
console.log("finished loading todos") | ||
} | ||
); | ||
) | ||
const todoJson = serialize(todoSchema, todo); | ||
const todoJson = serialize(todoSchema, todo) | ||
``` | ||
@@ -1123,3 +850,3 @@ | ||
function Todo(parentTodo) { | ||
this.parent = parentTodo; // available in subTasks | ||
this.parent = parentTodo // available in subTasks | ||
} | ||
@@ -1131,17 +858,17 @@ | ||
task: primitive(), | ||
owner: reference('_userId', UserStore.findUserById), // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias('children', list(object(todoSchema))), | ||
}, | ||
}; | ||
setDefaultModelSchema(Todo, todoSchema); | ||
owner: reference("_userId", UserStore.findUserById), // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias("children", list(object(todoSchema))) | ||
} | ||
} | ||
setDefaultModelSchema(Todo, todoSchema) | ||
const todo = deserialize( | ||
Todo, // just pass the constructor name, schema will be picked up | ||
{ task: 'grab coffee', owner: 17, children: [] }, | ||
{ task: "grab coffee", owner: 17, children: [] }, | ||
(err, todos) => { | ||
console.log('finished loading todos'); | ||
console.log("finished loading todos") | ||
} | ||
); | ||
) | ||
const todoJson = serialize(todo); // no need to pass schema explicitly | ||
const todoJson = serialize(todo) // no need to pass schema explicitly | ||
``` | ||
@@ -1156,14 +883,12 @@ | ||
createModelSchema(Todo, { | ||
task: primitive(), | ||
}); | ||
task: primitive() | ||
}) | ||
const todo = deserialize( | ||
Todo, // just pass the constructor name, schema will be picked up | ||
{ task: 'grab coffee', owner: 17, children: [] }, | ||
(err, todos) => { | ||
console.log('finished loading todos'); | ||
} | ||
); | ||
{ task: "grab coffee", owner: 17, children: [] }, | ||
(err, todos) => console.log("finished loading todos") | ||
) | ||
const todoJson = serialize(todo); // no need to pass schema explicitly | ||
const todoJson = serialize(todo) // no need to pass schema explicitly | ||
``` | ||
@@ -1176,9 +901,9 @@ | ||
@serializable(primitive()) | ||
task = 'Grab coffee'; | ||
task = "Grab coffee" | ||
@serializable(reference('_userId', UserStore.findUserById)) | ||
owner = null; | ||
@serializable(reference("_userId", UserStore.findUserById)) | ||
owner = null | ||
@serializable(alias('children', list(object(todoSchema)))) | ||
subTasks = []; | ||
@serializable(alias("children", list(object(todoSchema)))) | ||
subTasks = [] | ||
} | ||
@@ -1191,13 +916,11 @@ | ||
{ | ||
task: 'grab coffee', | ||
task: "grab coffee", | ||
owner: 17, | ||
children: [], | ||
}, | ||
children: [] | ||
} | ||
], | ||
(err, todos) => { | ||
console.log('finished loading todos'); | ||
} | ||
); | ||
(err, todos) => console.log("finished loading todos") | ||
) | ||
const todoJson = serialize(todos); | ||
const todoJson = serialize(todos) | ||
``` | ||
@@ -1208,9 +931,9 @@ | ||
```javascript | ||
const someTodoStoreById = {}; | ||
const someTodoStoreById = {} | ||
getDefaultModelSchema(Todo).factory = context => { | ||
const json = context.json; | ||
if (someTodoStoreById[json.id]) return someTodoStoreById[json.id]; // reuse instance | ||
return (someTodoStoreById[json.id] = new Todo()); | ||
}; | ||
const json = context.json | ||
if (someTodoStoreById[json.id]) return someTodoStoreById[json.id] // reuse instance | ||
return (someTodoStoreById[json.id] = new Todo()) | ||
} | ||
``` | ||
@@ -1232,18 +955,11 @@ | ||
createModelSchema(User, { username: true }, context => { | ||
return new User(context.args.someStore); | ||
}); | ||
return new User(context.args.someStore) | ||
}) | ||
// don't want singletons! | ||
const someStore = new SomeStore(); | ||
const someStore = new SomeStore() | ||
// provide somestore through context of the deserialization process | ||
const user = deserialize( | ||
User, | ||
someJson, | ||
(err, user) => { | ||
console.log('done'); | ||
}, | ||
{ | ||
someStore: someStore, | ||
} | ||
); | ||
const user = deserialize(User, someJson, (err, user) => console.log("done"), { | ||
someStore: someStore | ||
}) | ||
``` | ||
@@ -1255,23 +971,35 @@ | ||
// models.js: | ||
import { observable, computed } from 'mobx'; | ||
import { serializable, identifier } from 'serializr'; | ||
import { observable, computed } from "mobx" | ||
import { serializable, identifier } from "serializr" | ||
function randomId() { | ||
return Math.floor(Math.random() * 100000); | ||
return Math.floor(Math.random() * 100000) | ||
} | ||
export class Box { | ||
@serializable(identifier()) id = randomId(); | ||
@serializable @observable x = 0; | ||
@serializable @observable y = 0; | ||
@serializable @observable location = 0; | ||
@serializable(identifier()) | ||
id = randomId() | ||
@serializable | ||
@observable | ||
x = 0 | ||
@serializable | ||
@observable | ||
y = 0 | ||
@serializable | ||
@observable | ||
location = 0 | ||
constructor(location, x, y) { | ||
this.location = location; | ||
this.x = x; | ||
this.y = y; | ||
this.location = location | ||
this.x = x | ||
this.y = y | ||
} | ||
@serializable @computed get area() { | ||
return this.x * this.y; | ||
@serializable | ||
@computed | ||
get area() { | ||
return this.x * this.y | ||
} | ||
@@ -1281,18 +1009,16 @@ } | ||
export class Arrow { | ||
@serializable(identifier()) id = randomId(); | ||
@serializable(reference(Box)) from; | ||
@serializable(reference(Box)) to; | ||
@serializable(identifier()) | ||
id = randomId() | ||
@serializable(reference(Box)) | ||
from | ||
@serializable(reference(Box)) | ||
to | ||
} | ||
// store.js: | ||
import { observable, transaction } from 'mobx'; | ||
import { | ||
createSimpleSchema, | ||
identifier, | ||
list, | ||
serialize, | ||
deserialize, | ||
update, | ||
} from 'serializr'; | ||
import { Box, Arrow } from './models'; | ||
import { observable, transaction } from "mobx" | ||
import { createSimpleSchema, identifier, list, serialize, deserialize, update } from "serializr" | ||
import { Box, Arrow } from "./models" | ||
@@ -1303,4 +1029,4 @@ // The store that holds our domain: boxes and arrows | ||
arrows: [], | ||
selection: null, | ||
}); | ||
selection: null | ||
}) | ||
@@ -1311,8 +1037,8 @@ // Model of the store itself | ||
arrows: list(object(Arrow)), | ||
selection: reference(Box), | ||
}); | ||
selection: reference(Box) | ||
}) | ||
// Example Data | ||
// You can push data in as a class | ||
store.boxes.push(new Box('Rotterdam', 100, 100), new Box('Vienna', 650, 300)); | ||
store.boxes.push(new Box("Rotterdam", 100, 100), new Box("Vienna", 650, 300)) | ||
@@ -1323,8 +1049,8 @@ // Or it can be an raw javascript object with the right properties | ||
from: store.boxes[0], | ||
to: store.boxes[1], | ||
}); | ||
to: store.boxes[1] | ||
}) | ||
// (de) serialize functions | ||
function serializeState(store) { | ||
return serialize(storeModel, store); | ||
return serialize(storeModel, store) | ||
} | ||
@@ -1334,11 +1060,11 @@ | ||
transaction(() => { | ||
update(storeModel, store, json); | ||
}); | ||
update(storeModel, store, json) | ||
}) | ||
} | ||
// Print ... out for debugging | ||
console.dir(serializeState(store), { depth: 10, colors: true }); | ||
console.dir(serializeState(store), { depth: 10, colors: true }) | ||
``` | ||
* * * | ||
--- | ||
@@ -1345,0 +1071,0 @@ # Future ideas |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
594051
40
3205
33
2
1042
1