Comparing version 1.4.1 to 1.5.0
@@ -0,9 +1,15 @@ | ||
# 1.5.0 | ||
* Introduced enhanced debug messages on invariant errors, see #68 by @chengjianhua | ||
+ Fixed undefined should not throw error in Arraylike object, see #58 | ||
* switched build and tests to Babel 7 | ||
* switched code compression to [Terser](https://github.com/terser-js/terser) | ||
# 1.4.0 | ||
* Introduced beforeDeserialize and afterDeserialize lifecycle methods to support e.g. better error handling during deserialization by @evoye | ||
* Introduced cancelDeserialize to stop async requests that get stuck, e.g. in case of unresolved waiting references by @evoye | ||
* Added capability to deserialize arrays with empty entries by @evoye | ||
* Fixed mapAsArray to always push elements to the serialized array by @evoye | ||
* Introduced beforeDeserialize and afterDeserialize lifecycle methods to support e.g. better error handling during deserialization by @1R053 | ||
* Introduced cancelDeserialize to stop async requests that get stuck, e.g. in case of unresolved waiting references by @1R053 | ||
* Added capability to deserialize arrays with empty entries by @1R053 | ||
* Fixed mapAsArray to always push elements to the serialized array by @1R053 | ||
# 1.3.0 | ||
* Introduced async ability into `custom` (de)serializer, to support asynchronous custom deserialization by @evoye | ||
* Introduced async ability into `custom` (de)serializer, to support asynchronous custom deserialization by @1R053 | ||
* Fixed missed typescript export of `raw` type by @VChastinet | ||
@@ -10,0 +16,0 @@ * Documentation updates by @maslade and @razetdinov |
@@ -26,2 +26,43 @@ /** | ||
var formatters = { | ||
j: function json(v) { | ||
try { | ||
return JSON.stringify(v) | ||
} catch (error) { | ||
return "[UnexpectedJSONParseError]: " + error.message | ||
} | ||
} | ||
}; | ||
function invariant(condition, message) { | ||
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 formatter = formatters[format]; | ||
if (typeof formatter === "function") { | ||
var variable = variables[index++]; | ||
variablesToLog.push(variable); | ||
return formatter(variable) | ||
} | ||
return match | ||
}); | ||
if (console && variablesToLog.length > 0) { | ||
// eslint-disable-next-line no-console | ||
console.log.apply(console, variablesToLog); | ||
} | ||
throw new Error("[serializr] " + (formattedMessage || "Illegal State")) | ||
} | ||
} | ||
function GUARDED_NOOP(err) { | ||
@@ -43,7 +84,2 @@ if (err) // unguarded error... | ||
function invariant(cond, message) { | ||
if (!cond) | ||
throw new Error("[serializr] " + (message || "Illegal State")) | ||
} | ||
function parallel(ar, processor, cb) { | ||
@@ -201,3 +237,3 @@ // TODO: limit parallelization? | ||
}; | ||
// find super model | ||
// find super model | ||
if (clazz.prototype.constructor !== Object) { | ||
@@ -403,3 +439,3 @@ var s = getDefaultModelSchema(clazz.prototype.constructor); | ||
if (key === "*") { | ||
invariant(propDef === true, "prop schema '*' can onle be used with 'true'"); | ||
invariant(propDef === true, "prop schema '*' can only be used with 'true'"); | ||
serializeStarProps(schema, obj, res); | ||
@@ -583,2 +619,4 @@ return | ||
*/ | ||
/** | ||
@@ -599,2 +637,3 @@ * Cancels an asynchronous deserialization or update operation for the specified target object. | ||
*/ | ||
function schemaHasAlias(schema, name) { | ||
@@ -876,3 +915,3 @@ for (var key in schema.props) | ||
function date(additionalArgs) { | ||
// TODO: add format option? | ||
// TODO: add format option? | ||
var result = { | ||
@@ -1191,2 +1230,5 @@ serializer: function(value) { | ||
serializer: function (ar) { | ||
if (ar === undefined) { | ||
return SKIP | ||
} | ||
invariant(ar && "length" in ar && "map" in ar, "expected array (like) object"); | ||
@@ -1247,10 +1289,10 @@ return ar.map(propSchema.serializer) | ||
/** | ||
* 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. | ||
* | ||
* @param {*} propSchema | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
* 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. | ||
* | ||
* @param {*} propSchema | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function map(propSchema, additionalArgs) { | ||
@@ -1266,3 +1308,3 @@ propSchema = propSchema || _defaultPrimitiveProp; | ||
if (isMap) | ||
m.forEach(function(value, key) { | ||
m.forEach(function (value, key) { | ||
result[key] = propSchema.serializer(value); | ||
@@ -1274,3 +1316,3 @@ }); | ||
}, | ||
deserializer: function(jsonObject, done, context, oldValue) { | ||
deserializer: function (jsonObject, done, context, oldValue) { | ||
if (!jsonObject || typeof jsonObject !== "object") | ||
@@ -1280,27 +1322,27 @@ return void done("[serializr] expected JSON object") | ||
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 | ||
); | ||
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 | ||
); | ||
} | ||
@@ -1406,53 +1448,4 @@ }; | ||
*/ | ||
/** | ||
* JSDOC type defintions for usage w/o typescript. | ||
* @typedef {object} PropSchema | ||
* @property {serializerFunction} serializer | ||
* @property {deserializerFunction} deserializer | ||
* @property {boolean} identifier | ||
* | ||
* @typedef {object} PropertyDescriptor | ||
* @param {*} value | ||
* @param {boolean} writeable | ||
* @param {Function|undefined} get | ||
* @param {Function|undefined} set | ||
* @param {boolean} configurable | ||
* @param {boolean} enumerable | ||
* | ||
* @callback serializerFunction | ||
* @param {*} sourcePropertyValue | ||
* @returns any - serialized object | ||
* | ||
* | ||
* @callback deserializerFunction | ||
* @param {*} jsonValue | ||
* @param {cpsCallback} callback | ||
* @param {Context} context | ||
* @param {*} currentPropertyValue | ||
* @returns void | ||
* | ||
* @callback RegisterFunction | ||
* @param {*} id | ||
* @param {object} target | ||
* @param {Context} context | ||
* | ||
* @callback cpsCallback | ||
* @param {*} result | ||
* @param {*} error | ||
* @returns void | ||
* | ||
* @callback RefLookupFunction | ||
* @param {string} id | ||
* @param {cpsCallback} callback | ||
* @returns void | ||
* | ||
* @typedef {object} ModelSchema | ||
* @param factory | ||
* @param props | ||
* @param targetClass | ||
*/ | ||
// ~ deprecated | ||
export { createSimpleSchema, createModelSchema, getDefaultModelSchema, setDefaultModelSchema, serializable, serialize, serializeAll, cancelDeserialize, deserialize, update, primitive, identifier, date, alias, custom, object, reference, list, map, mapAsArray, raw, SKIP, object as child, reference as ref }; | ||
export { createSimpleSchema, createModelSchema, getDefaultModelSchema, setDefaultModelSchema, serializable, serialize, serializeAll, cancelDeserialize, deserialize, update, primitive, identifier, date, alias, custom, object, object as child, reference, reference as ref, list, map, mapAsArray, raw, SKIP }; |
2675
lib/serializr.js
/** serializr - (c) Michel Weststrate 2016 - MIT Licensed */ | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define('serializr', ['exports'], factory) : | ||
(factory((global.serializr = {}))); | ||
}(this, (function (exports) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define('serializr', ['exports'], factory) : | ||
(global = global || self, factory(global.serializr = {})); | ||
}(this, function (exports) { 'use strict'; | ||
/** | ||
* Creates a model schema that (de)serializes from / to plain javascript objects. | ||
* Its factory method is: `() => ({})` | ||
* | ||
* @example | ||
* var todoSchema = createSimpleSchema({ | ||
* title: true, | ||
* done: true, | ||
* }); | ||
* | ||
* var json = serialize(todoSchema, { title: 'Test', done: false }); | ||
* var todo = deserialize(todoSchema, json); | ||
* | ||
* @param {object} props property mapping, | ||
* @returns {object} model schema | ||
*/ | ||
function createSimpleSchema(props) { | ||
return { | ||
factory: function() { | ||
return {} | ||
}, | ||
props: props | ||
} | ||
} | ||
function GUARDED_NOOP(err) { | ||
if (err) // unguarded error... | ||
throw new Error(err) | ||
} | ||
function once(fn) { | ||
var fired = false; | ||
return function () { | ||
if (!fired) { | ||
fired = true; | ||
return fn.apply(null, arguments) | ||
/** | ||
* Creates a model schema that (de)serializes from / to plain javascript objects. | ||
* Its factory method is: `() => ({})` | ||
* | ||
* @example | ||
* var todoSchema = createSimpleSchema({ | ||
* title: true, | ||
* done: true, | ||
* }); | ||
* | ||
* var json = serialize(todoSchema, { title: 'Test', done: false }); | ||
* var todo = deserialize(todoSchema, json); | ||
* | ||
* @param {object} props property mapping, | ||
* @returns {object} model schema | ||
*/ | ||
function createSimpleSchema(props) { | ||
return { | ||
factory: function() { | ||
return {} | ||
}, | ||
props: props | ||
} | ||
invariant(false, "callback was invoked twice"); | ||
} | ||
} | ||
function invariant(cond, message) { | ||
if (!cond) | ||
throw new Error("[serializr] " + (message || "Illegal State")) | ||
} | ||
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 | ||
var resultArray = []; | ||
var failed = false; | ||
var processorCb = function (idx, err, result) { | ||
if (err) { | ||
if (!failed) { | ||
failed = true; | ||
cb(err); | ||
var formatters = { | ||
j: function json(v) { | ||
try { | ||
return JSON.stringify(v) | ||
} catch (error) { | ||
return "[UnexpectedJSONParseError]: " + error.message | ||
} | ||
} else { | ||
resultArray[idx] = result; | ||
if (--left === 0) | ||
cb(null, resultArray); | ||
} | ||
}; | ||
ar.forEach(function (value, idx) { | ||
processor(value, processorCb.bind(null, idx), idx); | ||
}); | ||
} | ||
function isPrimitive(value) { | ||
if (value === null) | ||
return true | ||
return typeof value !== "object" && typeof value !== "function" | ||
} | ||
function invariant(condition, message) { | ||
if (!condition) { | ||
var variables = Array.prototype.slice.call(arguments, 2); | ||
var variablesToLog = []; | ||
function isModelSchema(thing) { | ||
return thing && thing.factory && thing.props | ||
} | ||
var index = 0; | ||
var formattedMessage = message.replace(/%([a-zA-Z%])/g, function messageFormatter(match, format) { | ||
if (match === "%%") return match | ||
function isPropSchema(thing) { | ||
return thing && thing.serializer && thing.deserializer | ||
} | ||
var formatter = formatters[format]; | ||
function isAliasedPropSchema(propSchema) { | ||
return typeof propSchema === "object" && !!propSchema.jsonname | ||
} | ||
if (typeof formatter === "function") { | ||
var variable = variables[index++]; | ||
function isIdentifierPropSchema(propSchema) { | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
} | ||
variablesToLog.push(variable); | ||
function isAssignableTo(actualType, expectedType) { | ||
while (actualType) { | ||
if (actualType === expectedType) | ||
return true | ||
actualType = actualType.extends; | ||
return formatter(variable) | ||
} | ||
return match | ||
}); | ||
if (console && variablesToLog.length > 0) { | ||
// eslint-disable-next-line no-console | ||
console.log.apply(console, variablesToLog); | ||
} | ||
throw new Error("[serializr] " + (formattedMessage || "Illegal State")) | ||
} | ||
} | ||
return false | ||
} | ||
function isMapLike(thing) { | ||
return thing && typeof thing.keys === "function" && typeof thing.clear === "function" | ||
} | ||
function GUARDED_NOOP(err) { | ||
if (err) // unguarded error... | ||
throw new Error(err) | ||
} | ||
function getIdentifierProp(modelSchema) { | ||
invariant(isModelSchema(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; | ||
function once(fn) { | ||
var fired = false; | ||
return function () { | ||
if (!fired) { | ||
fired = true; | ||
return fn.apply(null, arguments) | ||
} | ||
invariant(false, "callback was invoked twice"); | ||
} | ||
} | ||
return null | ||
} | ||
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]; | ||
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 | ||
var resultArray = []; | ||
var failed = false; | ||
var processorCb = function (idx, 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); | ||
}); | ||
} | ||
return propSchema | ||
} | ||
/** | ||
* Returns the standard model schema associated with a class / constructor function | ||
* | ||
* @param {object} thing | ||
* @returns {ModelSchema} model schema | ||
*/ | ||
function getDefaultModelSchema(thing) { | ||
if (!thing) | ||
return null | ||
if (isModelSchema(thing)) | ||
return thing | ||
if (isModelSchema(thing.serializeInfo)) | ||
return thing.serializeInfo | ||
if (thing.constructor && thing.constructor.serializeInfo) | ||
return thing.constructor.serializeInfo | ||
} | ||
function isPrimitive(value) { | ||
if (value === null) | ||
return true | ||
return typeof value !== "object" && typeof value !== "function" | ||
} | ||
/** | ||
* 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. | ||
* | ||
* @param {constructor|class} clazz class or constructor function | ||
* @param {ModelSchema} modelSchema - a model schema | ||
* @returns {ModelSchema} model schema | ||
*/ | ||
function setDefaultModelSchema(clazz, modelSchema) { | ||
invariant(isModelSchema(modelSchema)); | ||
return clazz.serializeInfo = modelSchema | ||
} | ||
function isModelSchema(thing) { | ||
return thing && thing.factory && thing.props | ||
} | ||
/** | ||
* 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). | ||
* | ||
* @example | ||
* 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); | ||
* | ||
* @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 | ||
*/ | ||
function createModelSchema(clazz, props, factory) { | ||
invariant(clazz !== Object, "one cannot simply put define a model schema for Object"); | ||
invariant(typeof clazz === "function", "expected constructor function"); | ||
var model = { | ||
targetClass: clazz, | ||
factory: factory || function() { | ||
return new clazz() | ||
}, | ||
props: props | ||
}; | ||
// find super model | ||
if (clazz.prototype.constructor !== Object) { | ||
var s = getDefaultModelSchema(clazz.prototype.constructor); | ||
if (s && s.targetClass !== clazz) | ||
model.extends = s; | ||
function isPropSchema(thing) { | ||
return thing && thing.serializer && thing.deserializer | ||
} | ||
setDefaultModelSchema(clazz, model); | ||
return model | ||
} | ||
/** | ||
* Indicates that this field contains a primitive value (or Date) which should be serialized literally to json. | ||
* | ||
* @example | ||
* createModelSchema(Todo, { | ||
* title: primitive(), | ||
* }); | ||
* | ||
* console.dir(serialize(new Todo('test'))); | ||
* // outputs: { title : "test" } | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function primitive(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
invariant(isPrimitive(value), "this value is not primitive: " + 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) | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
function isAliasedPropSchema(propSchema) { | ||
return typeof propSchema === "object" && !!propSchema.jsonname | ||
} | ||
/** | ||
* 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. | ||
* | ||
* @example | ||
* 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 }); | ||
*/ | ||
var SKIP = typeof Symbol !== "undefined" ? Symbol("SKIP") : { SKIP: true }; | ||
function isIdentifierPropSchema(propSchema) { | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
} | ||
var _defaultPrimitiveProp = primitive(); | ||
function isAssignableTo(actualType, expectedType) { | ||
while (actualType) { | ||
if (actualType === expectedType) | ||
return true | ||
actualType = actualType.extends; | ||
} | ||
return false | ||
} | ||
// Ugly way to get the parameter names since they aren't easily retrievable via reflection | ||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; | ||
var ARGUMENT_NAMES = /([^\s,]+)/g; | ||
function isMapLike(thing) { | ||
return thing && typeof thing.keys === "function" && typeof thing.clear === "function" | ||
} | ||
function getParamNames(func) { | ||
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 | ||
} | ||
function getIdentifierProp(modelSchema) { | ||
invariant(isModelSchema(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; | ||
} | ||
return null | ||
} | ||
function serializableDecorator(propSchema, target, propName, descriptor) { | ||
invariant(arguments.length >= 2, "too few arguments. Please use @serializable as property decorator"); | ||
// Fix for @serializable used in class constructor params (typescript) | ||
var factory; | ||
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)"); | ||
var paramNames = getParamNames(target); | ||
if (paramNames.length >= descriptor) { | ||
propName = paramNames[descriptor]; | ||
propSchema.paramNumber = descriptor; | ||
descriptor = undefined; | ||
target = target.prototype; | ||
// Create a factory so the constructor is called properly | ||
factory = function(context) { | ||
var params = []; | ||
for (var i = 0; i < target.constructor.length; i++) { | ||
Object.keys(context.modelSchema.props).forEach(function (key) { | ||
var prop = context.modelSchema.props[key]; | ||
if (prop.paramNumber === i) { | ||
params[i] = context.json[prop.jsonname]; | ||
} | ||
}); | ||
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]; | ||
} | ||
return new (Function.prototype.bind.apply(target.constructor, [null].concat(params))) | ||
}; | ||
}); | ||
} | ||
return propSchema | ||
} | ||
invariant(typeof propName === "string", "incorrect usage of @serializable decorator"); | ||
var info = getDefaultModelSchema(target); | ||
if (!info || !target.constructor.hasOwnProperty("serializeInfo")) | ||
info = createModelSchema(target.constructor, {}, factory); | ||
if (info && info.targetClass !== target.constructor) | ||
// fixes typescript issue that tends to copy fields from super constructor to sub constructor in extends | ||
info = createModelSchema(target.constructor, {}, factory); | ||
info.props[propName] = propSchema; | ||
// MWE: why won't babel work without? | ||
if (descriptor && !descriptor.get && !descriptor.set) | ||
descriptor.writable = true; | ||
return descriptor | ||
} | ||
/** | ||
* Returns the standard model schema associated with a class / constructor function | ||
* | ||
* @param {object} thing | ||
* @returns {ModelSchema} model schema | ||
*/ | ||
function getDefaultModelSchema(thing) { | ||
if (!thing) | ||
return null | ||
if (isModelSchema(thing)) | ||
return thing | ||
if (isModelSchema(thing.serializeInfo)) | ||
return thing.serializeInfo | ||
if (thing.constructor && thing.constructor.serializeInfo) | ||
return thing.constructor.serializeInfo | ||
} | ||
/** | ||
* 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) { | ||
// decorated with propSchema | ||
var propSchema = arg1 === true ? _defaultPrimitiveProp : arg1; | ||
invariant(isPropSchema(propSchema), "@serializable expects prop schema"); | ||
return serializableDecorator.bind(null, propSchema) | ||
} else { | ||
// decorated without arguments, treat as primitive | ||
return serializableDecorator(primitive(), arg1, arg2, arg3) | ||
/** | ||
* 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. | ||
* | ||
* @param {constructor|class} clazz class or constructor function | ||
* @param {ModelSchema} modelSchema - a model schema | ||
* @returns {ModelSchema} model schema | ||
*/ | ||
function setDefaultModelSchema(clazz, modelSchema) { | ||
invariant(isModelSchema(modelSchema)); | ||
return clazz.serializeInfo = modelSchema | ||
} | ||
} | ||
/** | ||
* 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 modelschema to use. Optional | ||
* @param arg2 object(s) to serialize | ||
* @returns {object} serialized representation of the object | ||
*/ | ||
function serialize(arg1, 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 | ||
else if (!schema) | ||
schema = getDefaultModelSchema(thing[0]); | ||
} else if (!schema) { | ||
schema = getDefaultModelSchema(thing); | ||
/** | ||
* 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). | ||
* | ||
* @example | ||
* 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); | ||
* | ||
* @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 | ||
*/ | ||
function createModelSchema(clazz, props, factory) { | ||
invariant(clazz !== Object, "one cannot simply put define a model schema for Object"); | ||
invariant(typeof clazz === "function", "expected constructor function"); | ||
var model = { | ||
targetClass: clazz, | ||
factory: factory || function() { | ||
return new clazz() | ||
}, | ||
props: props | ||
}; | ||
// find super model | ||
if (clazz.prototype.constructor !== Object) { | ||
var s = getDefaultModelSchema(clazz.prototype.constructor); | ||
if (s && s.targetClass !== clazz) | ||
model.extends = s; | ||
} | ||
setDefaultModelSchema(clazz, model); | ||
return model | ||
} | ||
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) | ||
} | ||
function serializeWithSchema(schema, obj) { | ||
invariant(schema && typeof schema === "object", "Expected schema"); | ||
invariant(obj && typeof obj === "object", "Expected object"); | ||
var res; | ||
if (schema.extends) | ||
res = serializeWithSchema(schema.extends, obj); | ||
else { | ||
// TODO: make invariant?: invariant(!obj.constructor.prototype.constructor.serializeInfo, "object has a serializable supertype, but modelschema did not provide extends clause") | ||
res = {}; | ||
/** | ||
* Indicates that this field contains a primitive value (or Date) which should be serialized literally to json. | ||
* | ||
* @example | ||
* createModelSchema(Todo, { | ||
* title: primitive(), | ||
* }); | ||
* | ||
* console.dir(serialize(new Todo('test'))); | ||
* // outputs: { title : "test" } | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function primitive(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
invariant(isPrimitive(value), "this value is not primitive: " + 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) | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
Object.keys(schema.props).forEach(function (key) { | ||
var propDef = schema.props[key]; | ||
if (key === "*") { | ||
invariant(propDef === true, "prop schema '*' can onle be used with 'true'"); | ||
serializeStarProps(schema, obj, res); | ||
return | ||
/** | ||
* 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. | ||
* | ||
* @example | ||
* 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 }); | ||
*/ | ||
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 ARGUMENT_NAMES = /([^\s,]+)/g; | ||
function getParamNames(func) { | ||
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 | ||
} | ||
function serializableDecorator(propSchema, target, propName, descriptor) { | ||
invariant(arguments.length >= 2, "too few arguments. Please use @serializable as property decorator"); | ||
// Fix for @serializable used in class constructor params (typescript) | ||
var factory; | ||
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)"); | ||
var paramNames = getParamNames(target); | ||
if (paramNames.length >= descriptor) { | ||
propName = paramNames[descriptor]; | ||
propSchema.paramNumber = descriptor; | ||
descriptor = undefined; | ||
target = target.prototype; | ||
// Create a factory so the constructor is called properly | ||
factory = function(context) { | ||
var params = []; | ||
for (var i = 0; i < target.constructor.length; i++) { | ||
Object.keys(context.modelSchema.props).forEach(function (key) { | ||
var prop = context.modelSchema.props[key]; | ||
if (prop.paramNumber === i) { | ||
params[i] = context.json[prop.jsonname]; | ||
} | ||
}); | ||
} | ||
return new (Function.prototype.bind.apply(target.constructor, [null].concat(params))) | ||
}; | ||
} | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp; | ||
if (propDef === false) | ||
return | ||
var jsonValue = propDef.serializer(obj[key], key, obj); | ||
if (jsonValue === SKIP){ | ||
return | ||
invariant(typeof propName === "string", "incorrect usage of @serializable decorator"); | ||
var info = getDefaultModelSchema(target); | ||
if (!info || !target.constructor.hasOwnProperty("serializeInfo")) | ||
info = createModelSchema(target.constructor, {}, factory); | ||
if (info && info.targetClass !== target.constructor) | ||
// fixes typescript issue that tends to copy fields from super constructor to sub constructor in extends | ||
info = createModelSchema(target.constructor, {}, factory); | ||
info.props[propName] = propSchema; | ||
// MWE: why won't babel work without? | ||
if (descriptor && !descriptor.get && !descriptor.set) | ||
descriptor.writable = true; | ||
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) { | ||
// decorated with propSchema | ||
var propSchema = arg1 === true ? _defaultPrimitiveProp : arg1; | ||
invariant(isPropSchema(propSchema), "@serializable expects prop schema"); | ||
return serializableDecorator.bind(null, propSchema) | ||
} else { | ||
// decorated without arguments, treat as primitive | ||
return serializableDecorator(primitive(), arg1, arg2, arg3) | ||
} | ||
res[propDef.jsonname || key] = jsonValue; | ||
}); | ||
return res | ||
} | ||
} | ||
function serializeStarProps(schema, obj, target) { | ||
for (var key in obj) if (obj.hasOwnProperty(key)) if (!(key in schema.props)) { | ||
var value = obj[key]; | ||
// when serializing only serialize primitive props. Assumes other props (without schema) are local state that doesn't need serialization | ||
if (isPrimitive(value)) | ||
target[key] = value; | ||
/** | ||
* 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 modelschema to use. Optional | ||
* @param arg2 object(s) to serialize | ||
* @returns {object} serialized representation of the object | ||
*/ | ||
function serialize(arg1, 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 | ||
else if (!schema) | ||
schema = getDefaultModelSchema(thing[0]); | ||
} else if (!schema) { | ||
schema = getDefaultModelSchema(thing); | ||
} | ||
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) | ||
} | ||
} | ||
/** | ||
* The `serializeAll` decorator can be used on a class to signal that all primitive properties should be serialized automatically. | ||
* | ||
* @example | ||
* @serializeAll class Store { | ||
* a = 3; | ||
* b; | ||
* } | ||
* | ||
* const store = new Store(); | ||
* store.c = 5; | ||
* store.d = {}; | ||
* t.deepEqual(serialize(store), { a: 3, b: undefined, c: 5 }); | ||
*/ | ||
function serializeAll(target) { | ||
invariant(arguments.length === 1 && typeof target === "function", "@serializeAll can only be used as class decorator"); | ||
function serializeWithSchema(schema, obj) { | ||
invariant(schema && typeof schema === "object", "Expected schema"); | ||
invariant(obj && typeof obj === "object", "Expected object"); | ||
var res; | ||
if (schema.extends) | ||
res = serializeWithSchema(schema.extends, obj); | ||
else { | ||
// TODO: make invariant?: invariant(!obj.constructor.prototype.constructor.serializeInfo, "object has a serializable supertype, but modelschema did not provide extends clause") | ||
res = {}; | ||
} | ||
Object.keys(schema.props).forEach(function (key) { | ||
var propDef = schema.props[key]; | ||
if (key === "*") { | ||
invariant(propDef === true, "prop schema '*' can only be used with 'true'"); | ||
serializeStarProps(schema, obj, res); | ||
return | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp; | ||
if (propDef === false) | ||
return | ||
var jsonValue = propDef.serializer(obj[key], key, obj); | ||
if (jsonValue === SKIP){ | ||
return | ||
} | ||
res[propDef.jsonname || key] = jsonValue; | ||
}); | ||
return res | ||
} | ||
var info = getDefaultModelSchema(target); | ||
if (!info || !target.hasOwnProperty("serializeInfo")) { | ||
info = createModelSchema(target, {}); | ||
setDefaultModelSchema(target, info); | ||
function serializeStarProps(schema, obj, target) { | ||
for (var key in obj) if (obj.hasOwnProperty(key)) if (!(key in schema.props)) { | ||
var value = obj[key]; | ||
// when serializing only serialize primitive props. Assumes other props (without schema) are local state that doesn't need serialization | ||
if (isPrimitive(value)) | ||
target[key] = value; | ||
} | ||
} | ||
getDefaultModelSchema(target).props["*"] = true; | ||
return target | ||
} | ||
/** | ||
* The `serializeAll` decorator can be used on a class to signal that all primitive properties should be serialized automatically. | ||
* | ||
* @example | ||
* @serializeAll class Store { | ||
* a = 3; | ||
* b; | ||
* } | ||
* | ||
* const store = new Store(); | ||
* store.c = 5; | ||
* store.d = {}; | ||
* t.deepEqual(serialize(store), { a: 3, b: undefined, c: 5 }); | ||
*/ | ||
function serializeAll(target) { | ||
invariant(arguments.length === 1 && typeof target === "function", "@serializeAll can only be used as class decorator"); | ||
var rootContextCache = new Map(); | ||
var info = getDefaultModelSchema(target); | ||
if (!info || !target.hasOwnProperty("serializeInfo")) { | ||
info = createModelSchema(target, {}); | ||
setDefaultModelSchema(target, info); | ||
} | ||
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; | ||
getDefaultModelSchema(target).props["*"] = true; | ||
return target | ||
} | ||
} | ||
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); | ||
} | ||
} 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("\", \"") + | ||
"\"" | ||
)); | ||
var rootContextCache = new Map(); | ||
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; | ||
} | ||
} | ||
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); | ||
} 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 | ||
}, this).join("\", \"") + | ||
"\"" | ||
)); | ||
rootContextCache.delete(this); | ||
} else { | ||
this.onReadyCb(null, this.target); | ||
rootContextCache.delete(this); | ||
} | ||
} | ||
} | ||
}.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) | ||
} | ||
}.bind(this)) | ||
}; | ||
this.pendingRefsCount++; | ||
if (!this.pendingRefs[uuid]) | ||
this.pendingRefs[uuid] = []; | ||
this.pendingRefs[uuid].push({ | ||
modelSchema: modelSchema, | ||
uuid: uuid, | ||
callback: callback | ||
}); | ||
}; | ||
// 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); | ||
// 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); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
// 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); | ||
}; | ||
// 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)); | ||
// 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)); | ||
}); | ||
}); | ||
}); | ||
this.pendingRefs = {}; | ||
this.pendingRefsCount = 0; | ||
}; | ||
this.pendingRefs = {}; | ||
this.pendingRefsCount = 0; | ||
}; | ||
function getTargetContext(target) { | ||
return rootContextCache.get(target) | ||
} | ||
function getTargetContext(target) { | ||
return rootContextCache.get(target) | ||
} | ||
/* | ||
* Deserialization | ||
*/ | ||
/** | ||
* Cancels an asynchronous deserialization or update operation for the specified target object. | ||
* @param instance object that was previously returned from deserialize or update method | ||
*/ | ||
function cancelDeserialize(instance) { | ||
invariant(typeof instance === "object" && instance && !Array.isArray(instance), "cancelDeserialize needs an object"); | ||
var context = getTargetContext(instance); | ||
if (context) { | ||
context.cancelAwaits(); | ||
/* | ||
* Deserialization | ||
*/ | ||
/** | ||
* Cancels an asynchronous deserialization or update operation for the specified target object. | ||
* @param instance object that was previously returned from deserialize or update method | ||
*/ | ||
function cancelDeserialize(instance) { | ||
invariant(typeof instance === "object" && instance && !Array.isArray(instance), "cancelDeserialize needs an object"); | ||
var context = getTargetContext(instance); | ||
if (context) { | ||
context.cancelAwaits(); | ||
} | ||
} | ||
} | ||
/* | ||
* Deserialization | ||
*/ | ||
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 | ||
} | ||
/* | ||
* Deserialization | ||
*/ | ||
function deserializeStarProps(schema, obj, json) { | ||
for (var key in json) if (!(key in schema.props) && !schemaHasAlias(schema, key)) { | ||
var value = json[key]; | ||
// when deserializing we don't want to silently ignore 'unparseable data' to avoid | ||
// confusing bugs | ||
invariant(isPrimitive(value), | ||
"encountered non primitive value while deserializing '*' properties in property '" + | ||
key + "': " + value); | ||
obj[key] = value; | ||
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 | ||
} | ||
} | ||
/** | ||
* 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) { | ||
invariant(arguments.length >= 2, "deserialize expects at least 2 arguments"); | ||
schema = getDefaultModelSchema(schema); | ||
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) | ||
} | ||
function deserializeStarProps(schema, obj, json) { | ||
for (var key in json) if (!(key in schema.props) && !schemaHasAlias(schema, key)) { | ||
var value = json[key]; | ||
// when deserializing we don't want to silently ignore 'unparseable data' to avoid | ||
// confusing bugs | ||
invariant(isPrimitive(value), | ||
"encountered non primitive value while deserializing '*' properties in property '" + | ||
key + "': " + value); | ||
obj[key] = value; | ||
} | ||
} | ||
function deserializeObjectWithSchema(parentContext, modelSchema, json, callback, customArgs) { | ||
if (json === null || json === undefined || typeof json !== "object") | ||
return void callback(null, null) | ||
var context = new Context(parentContext, modelSchema, json, callback, customArgs); | ||
var target = modelSchema.factory(context); | ||
// todo async invariant | ||
invariant(!!target, "No object returned from factory"); | ||
// TODO: make invariant? invariant(schema.extends || | ||
// !target.constructor.prototype.constructor.serializeInfo, "object has a serializable | ||
// supertype, but modelschema did not provide extends clause") | ||
context.setTarget(target); | ||
var lock = context.createCallback(GUARDED_NOOP); | ||
deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
return target | ||
} | ||
/** | ||
* 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) { | ||
invariant(arguments.length >= 2, "deserialize expects at least 2 arguments"); | ||
schema = getDefaultModelSchema(schema); | ||
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) | ||
} | ||
function deserializePropsWithSchema(context, modelSchema, json, target) { | ||
if (modelSchema.extends) | ||
deserializePropsWithSchema(context, modelSchema.extends, json, target); | ||
function deserializeObjectWithSchema(parentContext, modelSchema, json, callback, customArgs) { | ||
if (json === null || json === undefined || typeof json !== "object") | ||
return void callback(null, null) | ||
var context = new Context(parentContext, modelSchema, json, callback, customArgs); | ||
var target = modelSchema.factory(context); | ||
// todo async invariant | ||
invariant(!!target, "No object returned from factory"); | ||
// TODO: make invariant? invariant(schema.extends || | ||
// !target.constructor.prototype.constructor.serializeInfo, "object has a serializable | ||
// supertype, but modelschema did not provide extends clause") | ||
context.setTarget(target); | ||
var lock = context.createCallback(GUARDED_NOOP); | ||
deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
return target | ||
} | ||
function deserializeProp(propDef, jsonValue, propName) { | ||
function deserializePropsWithSchema(context, modelSchema, json, target) { | ||
if (modelSchema.extends) | ||
deserializePropsWithSchema(context, modelSchema.extends, json, target); | ||
function setValue(value) { | ||
if (value !== SKIP) { | ||
target[propName] = value; | ||
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") { | ||
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); | ||
propDef.deserializer( | ||
finalOrRetryValue, | ||
preProcess(resultCallback), | ||
context, | ||
target[propName] | ||
); | ||
} else { | ||
resultCallback(errPreliminary, finalOrRetryValue); | ||
} | ||
} | ||
onAfterDeserialize(finalCallback, err, newValue, jsonValue, json, | ||
propName, context, propDef); | ||
} | ||
} | ||
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 | ||
); | ||
} | ||
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 | ||
); | ||
} | ||
Object.keys(modelSchema.props).forEach(function (propName) { | ||
var propDef = modelSchema.props[propName]; | ||
Object.keys(modelSchema.props).forEach(function (propName) { | ||
var propDef = modelSchema.props[propName]; | ||
function callbackDeserialize(err, jsonValue) { | ||
if (!err && jsonValue !== undefined) { | ||
deserializeProp(propDef, jsonValue, propName); | ||
} | ||
} | ||
function callbackDeserialize(err, jsonValue) { | ||
if (!err && jsonValue !== undefined) { | ||
deserializeProp(propDef, jsonValue, propName); | ||
if (propName === "*") { | ||
invariant(propDef === true, "prop schema '*' can only be used with 'true'"); | ||
deserializeStarProps(modelSchema, target, json); | ||
return | ||
} | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp; | ||
if (propDef === false) | ||
return | ||
var jsonAttr = propDef.jsonname || propName; | ||
var jsonValue = json[jsonAttr]; | ||
onBeforeDeserialize(callbackDeserialize, jsonValue, json, jsonAttr, context, propDef); | ||
}); | ||
} | ||
if (propName === "*") { | ||
invariant(propDef === true, "prop schema '*' can only be used with 'true'"); | ||
deserializeStarProps(modelSchema, target, json); | ||
return | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp; | ||
if (propDef === false) | ||
return | ||
var jsonAttr = propDef.jsonname || propName; | ||
var jsonValue = json[jsonAttr]; | ||
onBeforeDeserialize(callbackDeserialize, jsonValue, json, jsonAttr, context, propDef); | ||
}); | ||
} | ||
function onBeforeDeserialize( | ||
callback, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
function onBeforeDeserialize( | ||
callback, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
if (propDef && typeof propDef.beforeDeserialize === "function") { | ||
propDef.beforeDeserialize(callback, jsonValue, jsonParentValue, propNameOrIndex, context, | ||
propDef); | ||
} else { | ||
callback(null, jsonValue); | ||
if (propDef && typeof propDef.beforeDeserialize === "function") { | ||
propDef.beforeDeserialize(callback, jsonValue, jsonParentValue, propNameOrIndex, context, | ||
propDef); | ||
} else { | ||
callback(null, jsonValue); | ||
} | ||
} | ||
} | ||
function onAfterDeserialize( | ||
callback, err, newValue, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
function onAfterDeserialize( | ||
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 { | ||
callback(err, newValue); | ||
if (propDef && typeof propDef.afterDeserialize === "function") { | ||
propDef.afterDeserialize(callback, err, newValue, jsonValue, jsonParentValue, | ||
propNameOrIndex, context, propDef); | ||
} else { | ||
callback(err, newValue); | ||
} | ||
} | ||
} | ||
/* | ||
* Update | ||
*/ | ||
/* | ||
* Update | ||
*/ | ||
/** | ||
* 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 | ||
/** | ||
* 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 | ||
if (inferModelSchema) { | ||
target = arguments[0]; | ||
modelSchema = getDefaultModelSchema(target); | ||
json = arguments[1]; | ||
callback = arguments[2]; | ||
customArgs = arguments[3]; | ||
} else { | ||
modelSchema = getDefaultModelSchema(modelSchema); | ||
if (inferModelSchema) { | ||
target = arguments[0]; | ||
modelSchema = getDefaultModelSchema(target); | ||
json = arguments[1]; | ||
callback = arguments[2]; | ||
customArgs = arguments[3]; | ||
} else { | ||
modelSchema = getDefaultModelSchema(modelSchema); | ||
} | ||
invariant(isModelSchema(modelSchema), "update failed to determine schema"); | ||
invariant(typeof target === "object" && target && !Array.isArray(target), "update needs an object"); | ||
var context = new Context(null, modelSchema, json, callback, customArgs); | ||
context.setTarget(target); | ||
var lock = context.createCallback(GUARDED_NOOP); | ||
var result = deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
return result | ||
} | ||
invariant(isModelSchema(modelSchema), "update failed to determine schema"); | ||
invariant(typeof target === "object" && target && !Array.isArray(target), "update needs an object"); | ||
var context = new Context(null, modelSchema, json, callback, customArgs); | ||
context.setTarget(target); | ||
var lock = context.createCallback(GUARDED_NOOP); | ||
var result = deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
return result | ||
} | ||
function defaultRegisterFunction(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; | ||
if (typeof arg1 === "function") { | ||
registerFn = arg1; | ||
additionalArgs = arg2; | ||
} else { | ||
additionalArgs = arg1; | ||
function defaultRegisterFunction(id, value, context) { | ||
context.rootContext.resolve(context.modelSchema, id, context.target); | ||
} | ||
invariant(!additionalArgs || typeof additionalArgs === "object", "Additional property arguments should be an object, register function should be omitted or a funtion"); | ||
var result = { | ||
identifier: true, | ||
serializer: _defaultPrimitiveProp.serializer, | ||
deserializer: function (jsonValue, done, context) { | ||
_defaultPrimitiveProp.deserializer(jsonValue, function(err, id) { | ||
defaultRegisterFunction(id, context.target, context); | ||
if (registerFn) | ||
registerFn(id, context.target, context); | ||
done(err, id); | ||
}); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
/** | ||
* Similar to primitive, serializes instances of Date objects | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function date(additionalArgs) { | ||
// TODO: add format option? | ||
var result = { | ||
serializer: function(value) { | ||
if (value === null || value === undefined) | ||
return value | ||
invariant(value instanceof Date, "Expected Date object"); | ||
return value.getTime() | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
if (jsonValue === null || jsonValue === undefined) | ||
return void done(null, jsonValue) | ||
return void done(null, new Date(jsonValue)) | ||
/** | ||
* | ||
* | ||
* 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; | ||
if (typeof arg1 === "function") { | ||
registerFn = arg1; | ||
additionalArgs = arg2; | ||
} else { | ||
additionalArgs = arg1; | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
invariant(!additionalArgs || typeof additionalArgs === "object", "Additional property arguments should be an object, register function should be omitted or a funtion"); | ||
var result = { | ||
identifier: true, | ||
serializer: _defaultPrimitiveProp.serializer, | ||
deserializer: function (jsonValue, done, context) { | ||
_defaultPrimitiveProp.deserializer(jsonValue, function(err, id) { | ||
defaultRegisterFunction(id, context.target, context); | ||
if (registerFn) | ||
registerFn(id, context.target, context); | ||
done(err, id); | ||
}); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
/** | ||
* Alias indicates that this model property should be named differently in the generated json. | ||
* Alias should be the outermost propschema. | ||
* | ||
* @example | ||
* createModelSchema(Todo, { | ||
* title: alias('task', primitive()), | ||
* }); | ||
* | ||
* console.dir(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} | ||
*/ | ||
function alias(name, propSchema) { | ||
invariant(name && typeof name === "string", "expected prop name as first argument"); | ||
propSchema = (!propSchema || propSchema === true) ? _defaultPrimitiveProp : propSchema; | ||
invariant(isPropSchema(propSchema), "expected prop schema as second argument"); | ||
invariant(!isAliasedPropSchema(propSchema), "provided prop is already aliased"); | ||
return { | ||
jsonname: name, | ||
serializer: propSchema.serializer, | ||
deserializer: propSchema.deserializer, | ||
identifier: isIdentifierPropSchema(propSchema), | ||
beforeDeserialize: propSchema.beforeDeserialize, | ||
afterDeserialize: propSchema.afterDeserialize | ||
/** | ||
* Similar to primitive, serializes instances of Date objects | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function date(additionalArgs) { | ||
// TODO: add format option? | ||
var result = { | ||
serializer: function(value) { | ||
if (value === null || value === undefined) | ||
return value | ||
invariant(value instanceof Date, "Expected Date object"); | ||
return value.getTime() | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
if (jsonValue === null || jsonValue === undefined) | ||
return void done(null, jsonValue) | ||
return void done(null, new Date(jsonValue)) | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
} | ||
/** | ||
* 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. | ||
/** | ||
* Alias indicates that this model property should be named differently in the generated json. | ||
* Alias should be the outermost propschema. | ||
* | ||
* @example | ||
* createModelSchema(Todo, { | ||
* title: alias('task', primitive()), | ||
* }); | ||
* | ||
* console.dir(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} | ||
*/ | ||
function alias(name, propSchema) { | ||
invariant(name && typeof name === "string", "expected prop name as first argument"); | ||
propSchema = (!propSchema || propSchema === true) ? _defaultPrimitiveProp : propSchema; | ||
invariant(isPropSchema(propSchema), "expected prop schema as second argument"); | ||
invariant(!isAliasedPropSchema(propSchema), "provided prop is already aliased"); | ||
return { | ||
jsonname: name, | ||
serializer: propSchema.serializer, | ||
deserializer: propSchema.deserializer, | ||
identifier: isIdentifierPropSchema(propSchema), | ||
beforeDeserialize: propSchema.beforeDeserialize, | ||
afterDeserialize: propSchema.afterDeserialize | ||
} | ||
} | ||
* The `custom` function takes two parameters, the `serializer` function and the `deserializer` function. | ||
/** | ||
* 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 `serializer` function has the signature: | ||
* `(value, key, obj) => void` | ||
* The `custom` function takes two parameters, the `serializer` function and the `deserializer` function. | ||
* When serializing the object `{a: 1}` the `serializer` function will be called with `serializer(1, 'a', {a: 1})`. | ||
* The `serializer` function has the signature: | ||
* `(value, key, obj) => void` | ||
* The `deserializer` function has the following signature for synchronous processing | ||
* `(value, context, oldValue) => void` | ||
* When serializing the object `{a: 1}` the `serializer` function will be called with `serializer(1, 'a', {a: 1})`. | ||
* For asynchronous processing the function expects the following signature | ||
* `(value, context, oldValue, callback) => void` | ||
* The `deserializer` function has the following signature for synchronous processing | ||
* `(value, context, oldValue) => 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) | ||
* }; | ||
* For asynchronous processing the function expects the following signature | ||
* `(value, context, oldValue, callback) => void` | ||
* | ||
* @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"); | ||
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)); | ||
* 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"); | ||
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)); | ||
} | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
/** | ||
* `object` indicates that this property contains an object that needs to be (de)serialized | ||
* using its own model schema. | ||
* | ||
* 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. | ||
* | ||
* @example | ||
* class SubTask {} | ||
* class Todo {} | ||
* | ||
* createModelSchema(SubTask, { | ||
* title: true, | ||
* }); | ||
* createModelSchema(Todo, { | ||
* title: true, | ||
* subTask: object(SubTask), | ||
* }); | ||
* | ||
* const todo = deserialize(Todo, { | ||
* title: 'Task', | ||
* subTask: { | ||
* title: 'Sub task', | ||
* }, | ||
* }); | ||
* | ||
* @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} | ||
*/ | ||
function object(modelSchema, additionalArgs) { | ||
invariant(typeof modelSchema === "object" || typeof modelSchema === "function", "No modelschema provided. If you are importing it from another file be aware of circular dependencies."); | ||
var result = { | ||
serializer: function (item) { | ||
modelSchema = getDefaultModelSchema(modelSchema); | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema); | ||
if (item === null || item === undefined) | ||
return item | ||
return serialize(modelSchema, item) | ||
}, | ||
deserializer: function (childJson, done, context) { | ||
modelSchema = getDefaultModelSchema(modelSchema); | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema); | ||
if (childJson === null || childJson === undefined) | ||
return void done(null, childJson) | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done, additionalArgs) | ||
/** | ||
* `object` indicates that this property contains an object that needs to be (de)serialized | ||
* using its own model schema. | ||
* | ||
* 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. | ||
* | ||
* @example | ||
* class SubTask {} | ||
* class Todo {} | ||
* | ||
* createModelSchema(SubTask, { | ||
* title: true, | ||
* }); | ||
* createModelSchema(Todo, { | ||
* title: true, | ||
* subTask: object(SubTask), | ||
* }); | ||
* | ||
* const todo = deserialize(Todo, { | ||
* title: 'Task', | ||
* subTask: { | ||
* title: 'Sub task', | ||
* }, | ||
* }); | ||
* | ||
* @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} | ||
*/ | ||
function object(modelSchema, additionalArgs) { | ||
invariant(typeof modelSchema === "object" || typeof modelSchema === "function", "No modelschema provided. If you are importing it from another file be aware of circular dependencies."); | ||
var result = { | ||
serializer: function (item) { | ||
modelSchema = getDefaultModelSchema(modelSchema); | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema); | ||
if (item === null || item === undefined) | ||
return item | ||
return serialize(modelSchema, item) | ||
}, | ||
deserializer: function (childJson, done, context) { | ||
modelSchema = getDefaultModelSchema(modelSchema); | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema); | ||
if (childJson === null || childJson === undefined) | ||
return void done(null, childJson) | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done, additionalArgs) | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
function createDefaultRefLookup(modelSchema) { | ||
return function resolve(uuid, cb, context) { | ||
context.rootContext.await(modelSchema, uuid, cb); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
function createDefaultRefLookup(modelSchema) { | ||
return function resolve(uuid, cb, context) { | ||
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."); | ||
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(!lookupFn || typeof lookupFn === "function", "second argument should be a lookup function or additional arguments object"); | ||
if (typeof target === "string") | ||
childIdentifierAttribute = target; | ||
else { | ||
var modelSchema = getDefaultModelSchema(target); | ||
invariant(isModelSchema(modelSchema), "expected model schema or string as first argument for 'ref', got " + modelSchema); | ||
lookupFn = lookupFn || createDefaultRefLookup(modelSchema); | ||
childIdentifierAttribute = getIdentifierProp(modelSchema); | ||
invariant(!!childIdentifierAttribute, "provided model schema doesn't define an identifier() property and cannot be used by 'ref'."); | ||
/** | ||
* `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."); | ||
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(!lookupFn || typeof lookupFn === "function", "second argument should be a lookup function or additional arguments object"); | ||
if (typeof target === "string") | ||
childIdentifierAttribute = target; | ||
else { | ||
var modelSchema = getDefaultModelSchema(target); | ||
invariant(isModelSchema(modelSchema), "expected model schema or string as first argument for 'ref', got " + modelSchema); | ||
lookupFn = lookupFn || createDefaultRefLookup(modelSchema); | ||
childIdentifierAttribute = getIdentifierProp(modelSchema); | ||
invariant(!!childIdentifierAttribute, "provided model schema doesn't define an identifier() property and cannot be used by 'ref'."); | ||
} | ||
} | ||
var result = { | ||
serializer: function (item) { | ||
if (!initialized) | ||
initialize(); | ||
return item ? item[childIdentifierAttribute] : null | ||
}, | ||
deserializer: function(identifierValue, done, context) { | ||
if (!initialized) | ||
initialize(); | ||
if (identifierValue === null || identifierValue === undefined) | ||
done(null, identifierValue); | ||
else | ||
lookupFn(identifierValue, done, context); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
var result = { | ||
serializer: function (item) { | ||
if (!initialized) | ||
initialize(); | ||
return item ? item[childIdentifierAttribute] : null | ||
}, | ||
deserializer: function(identifierValue, done, context) { | ||
if (!initialized) | ||
initialize(); | ||
if (identifierValue === null || identifierValue === undefined) | ||
done(null, identifierValue); | ||
else | ||
lookupFn(identifierValue, done, context); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
/** | ||
* List indicates that this property contains a list of things. | ||
* Accepts a sub model schema to serialize the contents | ||
* | ||
* @example | ||
* class SubTask {} | ||
* class Task {} | ||
* class Todo {} | ||
* | ||
* createModelSchema(SubTask, { | ||
* title: true, | ||
* }); | ||
* createModelSchema(Todo, { | ||
* title: true, | ||
* subTask: list(object(SubTask)), | ||
* }); | ||
* | ||
* const todo = deserialize(Todo, { | ||
* title: 'Task', | ||
* subTask: [ | ||
* { | ||
* title: 'Sub task 1', | ||
* }, | ||
* ], | ||
* }); | ||
* | ||
* @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} | ||
*/ | ||
function list(propSchema, additionalArgs) { | ||
propSchema = propSchema || _defaultPrimitiveProp; | ||
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); | ||
invariant(!isAliasedPropSchema(propSchema), | ||
"provided prop is aliased, please put aliases first"); | ||
var result = { | ||
serializer: function (ar) { | ||
invariant(ar && "length" in ar && "map" in ar, "expected array (like) object"); | ||
return ar.map(propSchema.serializer) | ||
}, | ||
deserializer: function (jsonArray, done, context) { | ||
if (!Array.isArray(jsonArray)) | ||
return void done("[serializr] expected JSON array") | ||
/** | ||
* List indicates that this property contains a list of things. | ||
* Accepts a sub model schema to serialize the contents | ||
* | ||
* @example | ||
* class SubTask {} | ||
* class Task {} | ||
* class Todo {} | ||
* | ||
* createModelSchema(SubTask, { | ||
* title: true, | ||
* }); | ||
* createModelSchema(Todo, { | ||
* title: true, | ||
* subTask: list(object(SubTask)), | ||
* }); | ||
* | ||
* const todo = deserialize(Todo, { | ||
* title: 'Task', | ||
* subTask: [ | ||
* { | ||
* title: 'Sub task 1', | ||
* }, | ||
* ], | ||
* }); | ||
* | ||
* @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} | ||
*/ | ||
function list(propSchema, additionalArgs) { | ||
propSchema = propSchema || _defaultPrimitiveProp; | ||
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); | ||
invariant(!isAliasedPropSchema(propSchema), | ||
"provided prop is aliased, please put aliases first"); | ||
var result = { | ||
serializer: function (ar) { | ||
if (ar === undefined) { | ||
return SKIP | ||
} | ||
invariant(ar && "length" in ar && "map" in ar, "expected array (like) object"); | ||
return ar.map(propSchema.serializer) | ||
}, | ||
deserializer: function (jsonArray, done, context) { | ||
if (!Array.isArray(jsonArray)) | ||
return void done("[serializr] expected JSON array") | ||
function processItem(jsonValue, onItemDone, itemIndex) { | ||
function callbackBefore(err, value) { | ||
if (!err) { | ||
propSchema.deserializer(value, deserializeDone, context); | ||
} else { | ||
onItemDone(err); | ||
function processItem(jsonValue, onItemDone, itemIndex) { | ||
function callbackBefore(err, value) { | ||
if (!err) { | ||
propSchema.deserializer(value, deserializeDone, context); | ||
} else { | ||
onItemDone(err); | ||
} | ||
} | ||
} | ||
function deserializeDone(err, value) { | ||
if (typeof propSchema.afterDeserialize === "function") { | ||
onAfterDeserialize(callbackAfter, err, value, jsonValue, itemIndex, context, | ||
propSchema); | ||
} else { | ||
onItemDone(err, value); | ||
function deserializeDone(err, value) { | ||
if (typeof propSchema.afterDeserialize === "function") { | ||
onAfterDeserialize(callbackAfter, err, value, jsonValue, itemIndex, context, | ||
propSchema); | ||
} else { | ||
onItemDone(err, value); | ||
} | ||
} | ||
} | ||
function callbackAfter(errPreliminary, finalOrRetryValue) { | ||
if (errPreliminary && finalOrRetryValue !== undefined && | ||
typeof propSchema.afterDeserialize === "function") { | ||
function callbackAfter(errPreliminary, finalOrRetryValue) { | ||
if (errPreliminary && finalOrRetryValue !== undefined && | ||
typeof propSchema.afterDeserialize === "function") { | ||
propSchema.deserializer( | ||
finalOrRetryValue, | ||
deserializeDone, | ||
context | ||
); | ||
} else { | ||
onItemDone(errPreliminary, finalOrRetryValue); | ||
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 | ||
); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
parallel( | ||
jsonArray, | ||
processItem, | ||
done | ||
); | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
/** | ||
* 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. | ||
* | ||
* @param {*} propSchema | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function map(propSchema, additionalArgs) { | ||
propSchema = propSchema || _defaultPrimitiveProp; | ||
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); | ||
invariant(!isAliasedPropSchema(propSchema), "provided prop is aliased, please put aliases first"); | ||
var res = { | ||
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 | ||
}, | ||
deserializer: function (jsonObject, done, context, oldValue) { | ||
if (!jsonObject || typeof jsonObject !== "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 | ||
); | ||
} | ||
}; | ||
res = processAdditionalPropArgs(res, additionalArgs); | ||
return res | ||
} | ||
/** | ||
* 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. | ||
* | ||
* @param {*} propSchema | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function map(propSchema, additionalArgs) { | ||
propSchema = propSchema || _defaultPrimitiveProp; | ||
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); | ||
invariant(!isAliasedPropSchema(propSchema), "provided prop is aliased, please put aliases first"); | ||
var res = { | ||
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 | ||
}, | ||
deserializer: function(jsonObject, done, context, oldValue) { | ||
if (!jsonObject || typeof jsonObject !== "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 | ||
); | ||
} | ||
}; | ||
res = processAdditionalPropArgs(res, additionalArgs); | ||
return res | ||
} | ||
/** | ||
* 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. | ||
* | ||
* @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} | ||
*/ | ||
function mapAsArray(propSchema, keyPropertyName, additionalArgs) { | ||
propSchema = propSchema || _defaultPrimitiveProp; | ||
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); | ||
invariant(!!keyPropertyName, "expected key property name as second argument"); | ||
var res = { | ||
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]) | ||
} | ||
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 | ||
); | ||
} | ||
}; | ||
res = processAdditionalPropArgs(res, additionalArgs); | ||
return res | ||
} | ||
/** | ||
* 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. | ||
* | ||
* @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} | ||
*/ | ||
function mapAsArray(propSchema, keyPropertyName, additionalArgs) { | ||
propSchema = propSchema || _defaultPrimitiveProp; | ||
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); | ||
invariant(!!keyPropertyName, "expected key property name as second argument"); | ||
var res = { | ||
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]) | ||
/** | ||
* Indicates that this field is only need to putted in the serialized json or | ||
* deserialized instance, without any transformations. Stay with its original value | ||
* | ||
* @example | ||
* createModelSchema(Model, { | ||
* rawData: raw(), | ||
* }); | ||
* | ||
* console.dir(serialize(new Model({ rawData: { a: 1, b: [], c: {} } } }))); | ||
* // outputs: { rawData: { a: 1, b: [], c: {} } } } | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function raw(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
return value | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
return void done(null, jsonValue) | ||
} | ||
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 | ||
); | ||
} | ||
}; | ||
res = processAdditionalPropArgs(res, additionalArgs); | ||
return res | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
/** | ||
* Indicates that this field is only need to putted in the serialized json or | ||
* deserialized instance, without any transformations. Stay with its original value | ||
* | ||
* @example | ||
* createModelSchema(Model, { | ||
* rawData: raw(), | ||
* }); | ||
* | ||
* console.dir(serialize(new Model({ rawData: { a: 1, b: [], c: {} } } }))); | ||
* // outputs: { rawData: { a: 1, b: [], c: {} } } } | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function raw(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
return value | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
return void done(null, jsonValue) | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
/* | ||
* ## Managing model schemas | ||
*/ | ||
// ~ deprecated | ||
/* | ||
* ## Managing model schemas | ||
*/ | ||
exports.createSimpleSchema = createSimpleSchema; | ||
exports.createModelSchema = createModelSchema; | ||
exports.getDefaultModelSchema = getDefaultModelSchema; | ||
exports.setDefaultModelSchema = setDefaultModelSchema; | ||
exports.serializable = serializable; | ||
exports.serialize = serialize; | ||
exports.serializeAll = serializeAll; | ||
exports.cancelDeserialize = cancelDeserialize; | ||
exports.deserialize = deserialize; | ||
exports.update = update; | ||
exports.primitive = primitive; | ||
exports.identifier = identifier; | ||
exports.date = date; | ||
exports.alias = alias; | ||
exports.custom = custom; | ||
exports.object = object; | ||
exports.child = object; | ||
exports.reference = reference; | ||
exports.ref = reference; | ||
exports.list = list; | ||
exports.map = map; | ||
exports.mapAsArray = mapAsArray; | ||
exports.raw = raw; | ||
exports.SKIP = SKIP; | ||
/** | ||
* JSDOC type defintions for usage w/o typescript. | ||
* @typedef {object} PropSchema | ||
* @property {serializerFunction} serializer | ||
* @property {deserializerFunction} deserializer | ||
* @property {boolean} identifier | ||
* | ||
* @typedef {object} PropertyDescriptor | ||
* @param {*} value | ||
* @param {boolean} writeable | ||
* @param {Function|undefined} get | ||
* @param {Function|undefined} set | ||
* @param {boolean} configurable | ||
* @param {boolean} enumerable | ||
* | ||
* @callback serializerFunction | ||
* @param {*} sourcePropertyValue | ||
* @returns any - serialized object | ||
* | ||
* | ||
* @callback deserializerFunction | ||
* @param {*} jsonValue | ||
* @param {cpsCallback} callback | ||
* @param {Context} context | ||
* @param {*} currentPropertyValue | ||
* @returns void | ||
* | ||
* @callback RegisterFunction | ||
* @param {*} id | ||
* @param {object} target | ||
* @param {Context} context | ||
* | ||
* @callback cpsCallback | ||
* @param {*} result | ||
* @param {*} error | ||
* @returns void | ||
* | ||
* @callback RefLookupFunction | ||
* @param {string} id | ||
* @param {cpsCallback} callback | ||
* @returns void | ||
* | ||
* @typedef {object} ModelSchema | ||
* @param factory | ||
* @param props | ||
* @param targetClass | ||
*/ | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
// ~ deprecated | ||
exports.createSimpleSchema = createSimpleSchema; | ||
exports.createModelSchema = createModelSchema; | ||
exports.getDefaultModelSchema = getDefaultModelSchema; | ||
exports.setDefaultModelSchema = setDefaultModelSchema; | ||
exports.serializable = serializable; | ||
exports.serialize = serialize; | ||
exports.serializeAll = serializeAll; | ||
exports.cancelDeserialize = cancelDeserialize; | ||
exports.deserialize = deserialize; | ||
exports.update = update; | ||
exports.primitive = primitive; | ||
exports.identifier = identifier; | ||
exports.date = date; | ||
exports.alias = alias; | ||
exports.custom = custom; | ||
exports.object = object; | ||
exports.reference = reference; | ||
exports.list = list; | ||
exports.map = map; | ||
exports.mapAsArray = mapAsArray; | ||
exports.raw = raw; | ||
exports.SKIP = SKIP; | ||
exports.child = object; | ||
exports.ref = reference; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); | ||
})); |
/** serializr - (c) Michel Weststrate 2016 - MIT Licensed */ | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define("serializr",["exports"],n):n(e.serializr={})}(this,function(e){"use strict";function f(e){if(e)throw new Error(e)}function l(e,n){if(!e)throw new Error("[serializr] "+(n||"Illegal State"))}function t(e,t,r){if(0!==e.length){var i=e.filter(function(){return!0}).length,o=[],a=!1;e.forEach(function(e,n){t(e,function(e,n,t){n?a||(a=!0,r(n)):(o[e]=t,0==--i&&r(null,o))}.bind(null,n),n)})}else r(null,[])}function s(e){return null===e||"object"!=typeof e&&"function"!=typeof e}function u(e){return e&&e.factory&&e.props}function p(e){return e&&e.serializer&&e.deserializer}function r(e){return"object"==typeof e&&!!e.jsonname}function o(e,n){for(;e;){if(e===n)return!0;e=e["extends"]}return!1}function c(e){return e&&"function"==typeof e.keys&&"function"==typeof e.clear}function d(n,t){if(t){l(p(n),"expected a propSchema");["beforeDeserialize","afterDeserialize"].forEach(function(e){"function"==typeof t[e]&&(n[e]=t[e])})}return n}function h(e){return e?u(e)?e:u(e.serializeInfo)?e.serializeInfo:e.constructor&&e.constructor.serializeInfo?e.constructor.serializeInfo:void 0:null}function a(e,n){return l(u(n)),e.serializeInfo=n}function m(e,n,t){l(e!==Object,"one cannot simply put define a model schema for Object"),l("function"==typeof e,"expected constructor function");var r={targetClass:e,factory:t||function(){return new e},props:n};if(e.prototype.constructor!==Object){var i=h(e.prototype.constructor);i&&i.targetClass!==e&&(r["extends"]=i)}return a(e,r),r}function i(e){var n={serializer:function(e){return l(s(e),"this value is not primitive: "+e),e},deserializer:function(e,n){s(e)?n(null,e):n("[serializr] this value is not primitive: "+e)}};return n=d(n,e)}var y="undefined"!=typeof Symbol?Symbol("SKIP"):{SKIP:!0},g=i(),b=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,v=/([^\s,]+)/g;function z(e,n,t,r){var i;if(l(2<=arguments.length,"too few arguments. Please use @serializable as property decorator"),t===undefined&&"function"==typeof n&&n.prototype&&r!==undefined&&"number"==typeof r){l(p(e),"Constructor params must use alias(name)"),l(e.jsonname,"Constructor params must use alias(name)");var o=function s(e){var n=e.toString().replace(b,""),t=n.slice(n.indexOf("(")+1,n.indexOf(")")).match(v);return null===t&&(t=[]),t}(n);o.length>=r&&(t=o[r],e.paramNumber=r,r=undefined,n=n.prototype,i=function(t){for(var r=[],i=0;i<n.constructor.length;i++)Object.keys(t.modelSchema.props).forEach(function(e){var n=t.modelSchema.props[e];n.paramNumber===i&&(r[i]=t.json[n.jsonname])});return new(Function.prototype.bind.apply(n.constructor,[null].concat(r)))})}l("string"==typeof t,"incorrect usage of @serializable decorator");var a=h(n);return a&&n.constructor.hasOwnProperty("serializeInfo")||(a=m(n.constructor,{},i)),a&&a.targetClass!==n.constructor&&(a=m(n.constructor,{},i)),a.props[t]=e,!r||r.get||r.set||(r.writable=!0),r}function n(e,n){l(1===arguments.length||2===arguments.length,"serialize expects one or 2 arguments");var t=1===arguments.length?e:n,r=1===arguments.length?null:e;if(Array.isArray(t)){if(0===t.length)return[];r||(r=h(t[0]))}else r||(r=h(t));return l(!!r,"Failed to find default schema for "+e),Array.isArray(t)?t.map(function(e){return j(r,e)}):j(r,t)}function j(r,i){var a;return l(r&&"object"==typeof r,"Expected schema"),l(i&&"object"==typeof i,"Expected object"),a=r["extends"]?j(r["extends"],i):{},Object.keys(r.props).forEach(function(e){var n=r.props[e];if("*"===e)return l(!0===n,"prop schema '*' can onle be used with 'true'"),void function o(e,n,t){for(var r in n)if(n.hasOwnProperty(r)&&!(r in e.props)){var i=n[r];s(i)&&(t[r]=i)}}(r,i,a);if(!0===n&&(n=g),!1!==n){var t=n.serializer(i[e],e,i);t!==y&&(a[n.jsonname||e]=t)}}),a}var x=new Map;function R(e,n,t,r,i){this.parentContext=e,this.isRoot=!e,this.pendingCallbacks=0,this.pendingRefsCount=0,this.onReadyCb=r||f,this.json=t,this.target=null,this.hasError=!1,this.modelSchema=n,this.isRoot?((this.rootContext=this).args=i,this.pendingRefs={},this.resolvedRefs={}):(this.rootContext=e.rootContext,this.args=e.args)}function C(e,n){for(var t in e.props)if("object"==typeof e.props[t]&&e.props[t].jsonname===n)return!0;return!1}function w(e,n,t,r,i){if(null!==t&&t!==undefined&&"object"==typeof t){var o=new R(e,n,t,r,i),a=n.factory(o);l(!!a,"No object returned from factory"),o.setTarget(a);var s=o.createCallback(f);return S(o,n,t,a),s(),a}r(null,null)}function S(f,n,u,c){n["extends"]&&S(f,n["extends"],u,c),Object.keys(n.props).forEach(function(r){var i=n.props[r];if("*"===r)return l(!0===i,"prop schema '*' can only be used with 'true'"),void function o(e,n,t){for(var r in t)if(!(r in e.props||C(e,r))){var i=t[r];l(s(i),"encountered non primitive value while deserializing '*' properties in property '"+r+"': "+i),n[r]=i}}(n,c,u);if(!0===i&&(i=g),!1!==i){var e=i.jsonname||r;k(function a(e,n){e||n===undefined||function t(i,o,a){i.deserializer(o,function s(r){return function(e,n){D(function t(e,n){e&&n!==undefined&&"function"==typeof i.afterDeserialize?i.deserializer(n,s(r),f,c[a]):r(e,n)},e,n,o,u,a,f,i)}}(f.rootContext.createCallback(function n(e){e!==y&&(c[a]=e)})),f,c[a])}(i,n,r)},u[e],u,e,f,i)}})}function k(e,n,t,r,i,o){o&&"function"==typeof o.beforeDeserialize?o.beforeDeserialize(e,n,t,r,i,o):e(null,n)}function D(e,n,t,r,i,o,a,s){s&&"function"==typeof s.afterDeserialize?s.afterDeserialize(e,n,t,r,i,o,a,s):e(n,t)}function E(r,i){l("object"==typeof r||"function"==typeof r,"No modelschema provided. If you are importing it from another file be aware of circular dependencies.");var e={serializer:function(e){return l(u(r=h(r)),"expected modelSchema, got "+r),null===e||e===undefined?e:n(r,e)},deserializer:function(e,n,t){l(u(r=h(r)),"expected modelSchema, got "+r),null!==e&&e!==undefined?w(t,r,e,n,i):n(null,e)}};return e=d(e,i)}function A(r,i,e){l(!!r,"No modelschema provided. If you are importing it from another file be aware of circular dependencies.");var o,a=!1;function s(){if(a=!0,l("string"!=typeof r||i&&"function"==typeof i,"if the reference target is specified by attribute name, a lookup function is required"),l(!i||"function"==typeof i,"second argument should be a lookup function or additional arguments object"),"string"==typeof r)o=r;else{var e=h(r);l(u(e),"expected model schema or string as first argument for 'ref', got "+e),i=i||function n(r){return function(e,n,t){t.rootContext.await(r,e,n)}}(e),l(!!(o=function t(e){for(l(u(e));e;){for(var n in e.props)if("object"==typeof e.props[n]&&!0===e.props[n].identifier)return n;e=e["extends"]}return null}(e)),"provided model schema doesn't define an identifier() property and cannot be used by 'ref'.")}}"object"==typeof i&&e===undefined&&(e=i,i=undefined);var n={serializer:function(e){return a||s(),e?e[o]:null},deserializer:function(e,n,t){a||s(),null===e||e===undefined?n(null,e):i(e,n,t)}};return n=d(n,e)}function O(c,e){l(p(c=c||g),"expected prop schema as first argument"),l(!r(c),"provided prop is aliased, please put aliases first");var n={serializer:function(e){return l(e&&"length"in e&&"map"in e,"expected array (like) object"),e.map(c.serializer)},deserializer:function(e,n,f){Array.isArray(e)?t(e,function u(t,r,i){function o(e,n){"function"==typeof c.afterDeserialize?D(a,e,n,t,i,f,c):r(e,n)}function a(e,n){e&&n!==undefined&&"function"==typeof c.afterDeserialize?c.deserializer(n,o,f):r(e,n)}k(function s(e,n){e?r(e):c.deserializer(n,o,f)},t,e,i,f,c)},n):n("[serializr] expected JSON array")}};return n=d(n,e)}R.prototype.createCallback=function(t){return this.pendingCallbacks++,function r(e){var n=!1;return function(){if(!n)return n=!0,e.apply(null,arguments);l(!1,"callback was invoked twice")}}(function(e,n){e?this.hasError||(this.hasError=!0,this.onReadyCb(e),x["delete"](this)):this.hasError||(t(n),--this.pendingCallbacks===this.pendingRefsCount&&(0<this.pendingRefsCount?this.onReadyCb(new Error('Unresolvable references in json: "'+Object.keys(this.pendingRefs).filter(function(e){return 0<this.pendingRefs[e].length},this).join('", "')+'"')):this.onReadyCb(null,this.target),x["delete"](this)))}.bind(this))},R.prototype.await=function(n,e,t){if(l(this.isRoot),e in this.resolvedRefs){var r=this.resolvedRefs[e].filter(function(e){return o(e.modelSchema,n)})[0];if(r)return void t(null,r.value)}this.pendingRefsCount++,this.pendingRefs[e]||(this.pendingRefs[e]=[]),this.pendingRefs[e].push({modelSchema:n,uuid:e,callback:t})},R.prototype.resolve=function(e,n,t){if(l(this.isRoot),this.resolvedRefs[n]||(this.resolvedRefs[n]=[]),this.resolvedRefs[n].push({modelSchema:e,value:t}),n in this.pendingRefs)for(var r=this.pendingRefs[n].length-1;0<=r;r--){var i=this.pendingRefs[n][r];o(e,i.modelSchema)&&(this.pendingRefs[n].splice(r,1),this.pendingRefsCount--,i.callback(null,t))}},R.prototype.setTarget=function(e){this.isRoot&&this.target&&x["delete"](this.target),this.target=e,x.set(this.target,this)},R.prototype.cancelAwaits=function(){l(this.isRoot);var t=this;Object.keys(this.pendingRefs).forEach(function(n){t.pendingRefs[n].forEach(function(e){t.pendingRefsCount--,e.callback(new Error("Reference resolution canceled for "+n))})}),this.pendingRefs={},this.pendingRefsCount=0},e.createSimpleSchema=function I(e){return{factory:function(){return{}},props:e}},e.createModelSchema=m,e.getDefaultModelSchema=h,e.setDefaultModelSchema=a,e.serializable=function P(e,n,t){if(1!==arguments.length)return z(i(),e,n,t);var r=!0===e?g:e;return l(p(r),"@serializable expects prop schema"),z.bind(null,r)},e.serialize=n,e.serializeAll=function M(e){l(1===arguments.length&&"function"==typeof e,"@serializeAll can only be used as class decorator");var n=h(e);return n&&e.hasOwnProperty("serializeInfo")||a(e,n=m(e,{})),h(e).props["*"]=!0,e},e.cancelDeserialize=function N(e){l("object"==typeof e&&e&&!Array.isArray(e),"cancelDeserialize needs an object");var n=function t(e){return x.get(e)}(e);n&&n.cancelAwaits()},e.deserialize=function T(r,e,n,i){if(l(2<=arguments.length,"deserialize expects at least 2 arguments"),l(u(r=h(r)),"first argument should be model schema"),Array.isArray(e)){var o=[];return t(e,function(e,n){var t=w(null,r,e,n,i);o.push(t)},n||f),o}return w(null,r,e,n,i)},e.update=function K(e,n,t,r,i){2===arguments.length||"function"==typeof arguments[2]?(e=h(n=arguments[0]),t=arguments[1],r=arguments[2],i=arguments[3]):e=h(e),l(u(e),"update failed to determine schema"),l("object"==typeof n&&n&&!Array.isArray(n),"update needs an object");var o=new R(null,e,t,r,i);o.setTarget(n);var a=o.createCallback(f),s=S(o,e,t,n);return a(),s},e.primitive=i,e.identifier=function F(e,n){var o,t;l(!(t="function"==typeof e?(o=e,n):e)||"object"==typeof t,"Additional property arguments should be an object, register function should be omitted or a funtion");var r={identifier:!0,serializer:g.serializer,deserializer:function(e,t,i){g.deserializer(e,function(e,n){!function r(e,n,t){t.rootContext.resolve(t.modelSchema,e,t.target)}(n,i.target,i),o&&o(n,i.target,i),t(e,n)})}};return r=d(r,t)},e.date=function J(e){var n={serializer:function(e){return null===e||e===undefined?e:(l(e instanceof Date,"Expected Date object"),e.getTime())},deserializer:function(e,n){null!==e&&e!==undefined?n(null,new Date(e)):n(null,e)}};return n=d(n,e)},e.alias=function _(e,n){return l(e&&"string"==typeof e,"expected prop name as first argument"),l(p(n=n&&!0!==n?n:g),"expected prop schema as second argument"),l(!r(n),"provided prop is already aliased"),{jsonname:e,serializer:n.serializer,deserializer:n.deserializer,identifier:function t(e){return"object"==typeof e&&!0===e.identifier}(n),beforeDeserialize:n.beforeDeserialize,afterDeserialize:n.afterDeserialize}},e.custom=function q(e,i,o){l("function"==typeof e,"first argument should be function"),l("function"==typeof i,"second argument should be a function or promise");var n={serializer:e,deserializer:function(e,n,t,r){4===i.length?i(e,t,r,n,o):n(null,i(e,t,r,null,o))}};return n=d(n,o)},e.object=E,e.reference=A,e.list=O,e.map=function U(i,t){l(p(i=i||g),"expected prop schema as first argument"),l(!r(i),"provided prop is aliased, please put aliases first");var e={serializer:function(e){l(e&&"object"==typeof e,"expected object or Map");var n=c(e),t={};if(n)e.forEach(function(e,n){t[n]=i.serializer(e)});else for(var r in e)t[r]=i.serializer(e[r]);return t},deserializer:function(n,a,e,s){if(n&&"object"==typeof n){var f=Object.keys(n);O(i,t).deserializer(f.map(function(e){return n[e]}),function(e,n){if(e)a(e);else{var t,r=c(s);t=r?(s.clear(),s):{};for(var i=0,o=f.length;i<o;i++)r?t.set(f[i],n[i]):t[f[i]]=n[i];a(null,t)}},e)}else a("[serializr] expected JSON object")}};return e=d(e,t)},e.mapAsArray=function $(i,u,n){l(p(i=i||g),"expected prop schema as first argument"),l(!!u,"expected key property name as second argument");var e={serializer:function(e){l(e&&"object"==typeof e,"expected object or Map");var n=c(e),t=[];if(n)e.forEach(function(e){t.push(i.serializer(e))});else for(var r in e)t.push(i.serializer(e[r]));return t},deserializer:function(a,s,e,f){O(i,n).deserializer(a,function(e,n){if(e)s(e);else{var t,r=c(f);t=r?(f.clear(),f):{};for(var i=0,o=a.length;i<o;i++)r?t.set(n[i][u],n[i]):t[n[i][u].toString()]=n[i];s(null,t)}},e)}};return e=d(e,n)},e.raw=function B(e){var n={serializer:function(e){return e},deserializer:function(e,n){n(null,e)}};return n=d(n,e)},e.SKIP=y,e.child=E,e.ref=A,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!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 u(e){return"object"==typeof e&&!0===e.identifier}function c(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},v=m(),b=/((\/\/.*$)|(\/\*[\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 u=(a=t.toString().replace(b,""),null===(f=a.slice(a.indexOf("(")+1,a.indexOf(")")).match(z))&&(f=[]),f);u.length>=i&&(r=u[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 c=d(t);return c&&t.constructor.hasOwnProperty("serializeInfo")||(c=y(t.constructor,{},o)),c&&c.targetClass!==t.constructor&&(c=y(t.constructor,{},o)),c.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||(i=d(r[0]))}else i||(i=d(r));return n(!!i,"Failed to find default schema for "+e),Array.isArray(r)?r.map(function(e){return R(i,e)}):R(i,r)}function R(e,t){var r;return n(e&&"object"==typeof e,"Expected schema"),n(t&&"object"==typeof t,"Expected object"),r=e["extends"]?R(e["extends"],t):{},Object.keys(e.props).forEach(function(i){var a=e.props[i];if("*"===i)return n(!0===a,"prop schema '*' can only be used with 'true'"),void function(e,t,n){for(var r in t)if(t.hasOwnProperty(r)&&!(r in e.props)){var i=t[r];o(i)&&(n[r]=i)}}(e,t,r);if(!0===a&&(a=v),!1!==a){var s=a.serializer(t[i],i,t);s!==g&&(r[a.jsonname||i]=s)}}),r}var C=new Map;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 S(e,t){for(var n in e.props)if("object"==typeof e.props[n]&&e.props[n].jsonname===t)return!0;return!1}function k(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 u=s.createCallback(r);return A(s,t,i,f),u(),f}o(null,null)}function A(e,t,r,i){t["extends"]&&A(e,t["extends"],r,i),Object.keys(t.props).forEach(function(a){var s=t.props[a];if("*"===a)return n(!0===s,"prop schema '*' can only be used with 'true'"),void function(e,t,r){for(var i in r)if(!(i in e.props||S(e,i))){var a=r[i];n(o(a),"encountered non primitive value while deserializing '*' properties in property '"+i+"': "+a),t[i]=a}}(t,i,r);if(!0===s&&(s=v),!1!==s){var f=s.jsonname||a;E(function(t,n){t||n===undefined||function(t,n,o){t.deserializer(n,function a(s){return function(f,u){D(function(n,r){n&&r!==undefined&&"function"==typeof t.afterDeserialize?t.deserializer(r,a(s),e,i[o]):s(n,r)},f,u,n,r,o,e,t)}}(e.rootContext.createCallback(function(e){e!==g&&(i[o]=e)})),e,i[o])}(s,n,a)},r[f],r,f,e,s)}})}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 O(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?k(o,e,r,i,t):i(null,r)}};return r=p(r,t)}function I(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 N(e,t){n(s(e=e||v),"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),C["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('", "')+'"')),C["delete"](this)):(this.onReadyCb(null,this.target),C["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 c(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];c(e,o.modelSchema)&&(this.pendingRefs[t].splice(i,1),this.pendingRefsCount--,o.callback(null,r))}},w.prototype.setTarget=function(e){this.isRoot&&this.target&&C["delete"](this.target),this.target=e,C.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?v: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){n(1===arguments.length&&"function"==typeof e,"@serializeAll can only be used as class decorator");var t=d(e);return t&&e.hasOwnProperty("serializeInfo")||h(e,t=y(e,{})),d(e).props["*"]=!0,e},e.cancelDeserialize=function(e){n("object"==typeof e&&e&&!Array.isArray(e),"cancelDeserialize needs an object");var t,r=(t=e,C.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=k(null,e,t,n,s);f.push(r)},o||r),f}return k(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 u=f.createCallback(r),c=A(f,e,i,t);return u(),c},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:v.serializer,deserializer:function(e,t,n){v.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:v),"expected prop schema as second argument"),n(!f(t),"provided prop is already aliased"),{jsonname:e,serializer:t.serializer,deserializer:t.deserializer,identifier:u(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=O,e.child=O,e.reference=I,e.ref=I,e.list=N,e.map=function(e,t){n(s(e=e||v),"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);N(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||v),"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){N(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,u=n.length;f<u;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})}); | ||
//# sourceMappingURL=serializr.min.js.map |
{ | ||
"name": "serializr", | ||
"version": "1.4.1", | ||
"version": "1.5.0", | ||
"description": "Serialize and deserialize complex object graphs to JSON", | ||
@@ -40,22 +40,21 @@ "main": "lib/serializr.js", | ||
"devDependencies": { | ||
"babel-cli": "^6.11.4", | ||
"babel-eslint": "^7.2.3", | ||
"babel-plugin-transform-decorators-legacy": "^1.3.4", | ||
"babel-preset-es2015": "^6.9.0", | ||
"babel-preset-stage-1": "^6.5.0", | ||
"coveralls": "^2.11.9", | ||
"documentation": "^4.0.0-beta9", | ||
"eslint": "^2.12.0", | ||
"eslint-plugin-import": "^2.7.0", | ||
"@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", | ||
"rollup": "^0.49.2", | ||
"rollup-plugin-commonjs": "^8.2.0", | ||
"rollup-plugin-node-resolve": "^3.0.0", | ||
"rollup-plugin-uglify": "^2.0.1", | ||
"tape": "^4.5.1", | ||
"typescript": "^2.1.4", | ||
"uglify-js": "^2.6.4" | ||
"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" | ||
} | ||
} | ||
} |
201
README.md
@@ -9,3 +9,3 @@ # Serializr | ||
_Serializr is feature complete, and easily extendable. Since there are no active maintainers the project is frozen feature wise. Bug reports are welcome and will be addressed._ | ||
_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._ | ||
@@ -197,8 +197,14 @@ Want to maintain a small open source project or having great ideas for this project? We are looking for maintainers, so [apply](https://github.com/mobxjs/serializr/issues/46)! | ||
**Babel 5.x** | ||
**Babel 7.x:** | ||
Install support for decorators: `npm i --save-dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators`. And enable it in your `.babelrc` file: | ||
```json | ||
{ | ||
"stage": 1 | ||
} | ||
{ | ||
"presets": ["@babel/preset-env"], | ||
"plugins": [ | ||
["@babel/plugin-proposal-decorators", { "legacy": true}], | ||
["@babel/plugin-proposal-class-properties", { "loose": true}] | ||
] | ||
} | ||
``` | ||
@@ -217,2 +223,10 @@ | ||
**Babel 5.x** | ||
```json | ||
{ | ||
"stage": 1 | ||
} | ||
``` | ||
Probably you have more plugins and presets in your `.babelrc` already, note that the order is important and `transform-decorators-legacy` should come as first. | ||
@@ -340,5 +354,66 @@ | ||
### Table of Contents | ||
- [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) | ||
- [cancelDeserialize](#canceldeserialize) | ||
- [Parameters](#parameters-8) | ||
- [deserialize](#deserialize) | ||
- [Parameters](#parameters-9) | ||
- [update](#update) | ||
- [Parameters](#parameters-10) | ||
- [primitive](#primitive) | ||
- [Parameters](#parameters-11) | ||
- [Examples](#examples-3) | ||
- [identifier](#identifier) | ||
- [Parameters](#parameters-12) | ||
- [Examples](#examples-4) | ||
- [date](#date) | ||
- [Parameters](#parameters-13) | ||
- [alias](#alias) | ||
- [Parameters](#parameters-14) | ||
- [Examples](#examples-5) | ||
- [custom](#custom) | ||
- [Parameters](#parameters-15) | ||
- [Examples](#examples-6) | ||
- [object](#object) | ||
- [Parameters](#parameters-16) | ||
- [Examples](#examples-7) | ||
- [reference](#reference) | ||
- [Parameters](#parameters-17) | ||
- [Examples](#examples-8) | ||
- [list](#list) | ||
- [Parameters](#parameters-18) | ||
- [Examples](#examples-9) | ||
- [map](#map) | ||
- [Parameters](#parameters-19) | ||
- [mapAsArray](#mapasarray) | ||
- [Parameters](#parameters-20) | ||
- [raw](#raw) | ||
- [Parameters](#parameters-21) | ||
- [Examples](#examples-10) | ||
- [SKIP](#skip) | ||
- [Examples](#examples-11) | ||
## ModelSchema | ||
[src/serializr.js:52-52](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/serializr.js#L52-L52 "Source code on GitHub") | ||
[src/serializr.js:52-52](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/serializr.js#L5-L51 "Source code on GitHub") | ||
@@ -349,3 +424,3 @@ JSDOC type defintions for usage w/o typescript. | ||
**Parameters** | ||
### Parameters | ||
@@ -374,3 +449,3 @@ - `value` **any** | ||
**Properties** | ||
### Properties | ||
@@ -391,3 +466,3 @@ - `serializer` **serializerFunction** | ||
[src/api/createSimpleSchema.js:17-24](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/createSimpleSchema.js#L17-L24 "Source code on GitHub") | ||
[src/api/createSimpleSchema.js:17-24](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/api/createSimpleSchema.js#L17-L24 "Source code on GitHub") | ||
@@ -397,7 +472,7 @@ Creates a model schema that (de)serializes from / to plain javascript objects. | ||
**Parameters** | ||
### Parameters | ||
- `props` **[object](#object)** property mapping, | ||
**Examples** | ||
### Examples | ||
@@ -418,3 +493,3 @@ ```javascript | ||
[src/api/createModelSchema.js:29-47](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/createModelSchema.js#L29-L47 "Source code on GitHub") | ||
[src/api/createModelSchema.js:29-47](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/api/createModelSchema.js#L29-L47 "Source code on GitHub") | ||
@@ -425,3 +500,3 @@ Creates a model schema that (de)serializes an object created by a constructor function (class). | ||
**Parameters** | ||
### Parameters | ||
@@ -432,3 +507,3 @@ - `clazz` **([constructor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
**Examples** | ||
### Examples | ||
@@ -454,7 +529,7 @@ ```javascript | ||
[src/api/getDefaultModelSchema.js:9-18](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/getDefaultModelSchema.js#L9-L18 "Source code on GitHub") | ||
[src/api/getDefaultModelSchema.js:9-18](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/api/getDefaultModelSchema.js#L9-L18 "Source code on GitHub") | ||
Returns the standard model schema associated with a class / constructor function | ||
**Parameters** | ||
### Parameters | ||
@@ -467,3 +542,3 @@ - `thing` **[object](#object)** | ||
[src/api/setDefaultModelSchema.js:15-18](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/setDefaultModelSchema.js#L15-L18 "Source code on GitHub") | ||
[src/api/setDefaultModelSchema.js:15-18](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/api/setDefaultModelSchema.js#L15-L18 "Source code on GitHub") | ||
@@ -477,3 +552,3 @@ Sets the default model schema for class / constructor function. | ||
**Parameters** | ||
### Parameters | ||
@@ -487,3 +562,3 @@ - `clazz` **([constructor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
[src/api/serializable.js:93-103](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/serializable.js#L93-L103 "Source code on GitHub") | ||
[src/api/serializable.js:93-103](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/api/serializable.js#L93-L103 "Source code on GitHub") | ||
@@ -496,3 +571,3 @@ Decorator that defines a new property mapping on the default model schema for the class | ||
**Parameters** | ||
### Parameters | ||
@@ -503,3 +578,3 @@ - `arg1` | ||
**Examples** | ||
### Examples | ||
@@ -514,3 +589,3 @@ ```javascript | ||
[src/core/serialize.js:16-34](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/serialize.js#L16-L34 "Source code on GitHub") | ||
[src/core/serialize.js:16-34](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/core/serialize.js#L16-L34 "Source code on GitHub") | ||
@@ -521,3 +596,3 @@ Serializes an object (graph) into json using the provided model schema. | ||
**Parameters** | ||
### Parameters | ||
@@ -531,7 +606,7 @@ - `arg1` modelschema to use. Optional | ||
[src/core/serialize.js:89-100](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/serialize.js#L89-L100 "Source code on GitHub") | ||
[src/core/serialize.js:89-100](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/core/serialize.js#L89-L100 "Source code on GitHub") | ||
The `serializeAll` decorator can be used on a class to signal that all primitive properties should be serialized automatically. | ||
**Parameters** | ||
### Parameters | ||
@@ -542,7 +617,7 @@ - `target` | ||
[src/core/cancelDeserialize.js:12-18](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/cancelDeserialize.js#L12-L18 "Source code on GitHub") | ||
[src/core/cancelDeserialize.js:12-18](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/core/cancelDeserialize.js#L12-L18 "Source code on GitHub") | ||
Cancels an asynchronous deserialization or update operation for the specified target object. | ||
**Parameters** | ||
### Parameters | ||
@@ -553,3 +628,3 @@ - `instance` object that was previously returned from deserialize or update method | ||
[src/core/deserialize.js:45-63](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/deserialize.js#L45-L63 "Source code on GitHub") | ||
[src/core/deserialize.js:45-63](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/core/deserialize.js#L45-L63 "Source code on GitHub") | ||
@@ -562,3 +637,3 @@ Deserializes a json structure into an object graph. | ||
**Parameters** | ||
### Parameters | ||
@@ -578,3 +653,3 @@ - `schema` **([object](#object) \| [array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array))** to use for deserialization | ||
[src/core/update.js:22-44](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/update.js#L22-L44 "Source code on GitHub") | ||
[src/core/update.js:22-44](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/core/update.js#L22-L44 "Source code on GitHub") | ||
@@ -585,3 +660,3 @@ Similar to deserialize, but updates an existing object instance. | ||
**Parameters** | ||
### Parameters | ||
@@ -598,11 +673,11 @@ - `modelSchema` **[object](#object)** , optional if it can be inferred from the instance type | ||
[src/types/primitive.js:18-32](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/primitive.js#L18-L32 "Source code on GitHub") | ||
[src/types/primitive.js:18-32](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/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** | ||
### Parameters | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
**Examples** | ||
### Examples | ||
@@ -622,3 +697,3 @@ ```javascript | ||
[src/types/identifier.js:43-66](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/identifier.js#L43-L66 "Source code on GitHub") | ||
[src/types/identifier.js:43-66](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/identifier.js#L43-L66 "Source code on GitHub") | ||
@@ -633,3 +708,3 @@ Similar to primitive, but this field will be marked as the identifier for the given Model type. | ||
**Parameters** | ||
### Parameters | ||
@@ -639,3 +714,3 @@ - `arg1` **(RegisterFunction | AdditionalPropArgs)** optional registerFn: function to register this object during creation. | ||
**Examples** | ||
### Examples | ||
@@ -666,7 +741,7 @@ ```javascript | ||
[src/types/date.js:9-26](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/date.js#L9-L26 "Source code on GitHub") | ||
[src/types/date.js:9-26](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/date.js#L9-L26 "Source code on GitHub") | ||
Similar to primitive, serializes instances of Date objects | ||
**Parameters** | ||
### Parameters | ||
@@ -679,3 +754,3 @@ - `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
[src/types/alias.js:20-33](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/alias.js#L20-L33 "Source code on GitHub") | ||
[src/types/alias.js:20-33](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/alias.js#L20-L33 "Source code on GitHub") | ||
@@ -685,3 +760,3 @@ Alias indicates that this model property should be named differently in the generated json. | ||
**Parameters** | ||
### Parameters | ||
@@ -691,3 +766,3 @@ - `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** name of the json field to be used for this property | ||
**Examples** | ||
### Examples | ||
@@ -707,3 +782,3 @@ ```javascript | ||
[src/types/custom.js:60-75](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/custom.js#L60-L75 "Source code on GitHub") | ||
[src/types/custom.js:60-75](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/custom.js#L60-L75 "Source code on GitHub") | ||
@@ -727,3 +802,3 @@ 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. | ||
**Parameters** | ||
### Parameters | ||
@@ -734,3 +809,3 @@ - `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 | ||
**Examples** | ||
### Examples | ||
@@ -775,3 +850,3 @@ ```javascript | ||
[src/types/object.js:35-55](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/object.js#L35-L55 "Source code on GitHub") | ||
[src/types/object.js:35-55](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/object.js#L35-L55 "Source code on GitHub") | ||
@@ -783,3 +858,3 @@ `object` indicates that this property contains an object that needs to be (de)serialized | ||
**Parameters** | ||
### Parameters | ||
@@ -789,3 +864,3 @@ - `modelSchema` **[ModelSchema](#modelschema)** to be used to (de)serialize the object | ||
**Examples** | ||
### Examples | ||
@@ -816,3 +891,3 @@ ```javascript | ||
[src/types/reference.js:66-105](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/reference.js#L66-L105 "Source code on GitHub") | ||
[src/types/reference.js:66-105](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/reference.js#L66-L105 "Source code on GitHub") | ||
@@ -836,3 +911,3 @@ `reference` can be used to (de)serialize references that point to other models. | ||
**Parameters** | ||
### Parameters | ||
@@ -843,3 +918,3 @@ - `target` : ModelSchema or string | ||
**Examples** | ||
### Examples | ||
@@ -884,3 +959,3 @@ ```javascript | ||
[src/types/list.js:42-104](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/list.js#L42-L104 "Source code on GitHub") | ||
[src/types/list.js:43-105](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/list.js#L43-L105 "Source code on GitHub") | ||
@@ -890,3 +965,3 @@ List indicates that this property contains a list of things. | ||
**Parameters** | ||
### Parameters | ||
@@ -896,3 +971,3 @@ - `propSchema` **PropSchema** to be used to (de)serialize the contents of the array | ||
**Examples** | ||
### Examples | ||
@@ -926,3 +1001,3 @@ ```javascript | ||
[src/types/map.js:14-65](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/map.js#L14-L65 "Source code on GitHub") | ||
[src/types/map.js:14-65](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/map.js#L14-L65 "Source code on GitHub") | ||
@@ -933,3 +1008,3 @@ Similar to list, but map represents a string keyed dynamic collection. | ||
**Parameters** | ||
### Parameters | ||
@@ -943,3 +1018,3 @@ - `propSchema` **any** | ||
[src/types/mapAsArray.js:19-66](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/mapAsArray.js#L19-L66 "Source code on GitHub") | ||
[src/types/mapAsArray.js:19-66](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/mapAsArray.js#L19-L66 "Source code on GitHub") | ||
@@ -954,3 +1029,3 @@ Similar to map, mapAsArray can be used to serialize a map-like collection where the key is | ||
**Parameters** | ||
### Parameters | ||
@@ -965,3 +1040,3 @@ - `propSchema` **any** | ||
[src/types/raw.js:18-29](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/raw.js#L18-L29 "Source code on GitHub") | ||
[src/types/raw.js:18-29](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/types/raw.js#L18-L29 "Source code on GitHub") | ||
@@ -971,7 +1046,7 @@ Indicates that this field is only need to putted in the serialized json or | ||
**Parameters** | ||
### Parameters | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
**Examples** | ||
### Examples | ||
@@ -991,7 +1066,7 @@ ```javascript | ||
[src/constants.js:20-20](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/constants.js#L20-L20 "Source code on GitHub") | ||
[src/constants.js:20-20](https://github.com/mobxjs/serializr/blob/f3d8441271009ff1818c2893e7a18da92bbde541/src/constants.js#L20-L20 "Source code on GitHub") | ||
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. | ||
**Examples** | ||
### Examples | ||
@@ -998,0 +1073,0 @@ ```javascript |
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
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
256840
18
1280
2827
1