Comparing version 1.3.0 to 1.4.1
@@ -0,5 +1,15 @@ | ||
# 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 | ||
# 1.3.0 | ||
* Introduced async ability into `custom` (de)serializer, to support asynchronous custom deserialization by @evoye | ||
* Fixed missed typescript export of `raw` type by @VChastinet | ||
* Documentation updates by @maslade and @razetdinov | ||
# 1.2.1 | ||
* Introduced `raw` type, to allow for complex dynamic types to be deserialized | ||
* Introduced `customAsync`, to support asynchronous custom deserialization | ||
# 1.1.12 | ||
@@ -6,0 +16,0 @@ |
@@ -33,3 +33,3 @@ /** | ||
var fired = false; | ||
return function() { | ||
return function () { | ||
if (!fired) { | ||
@@ -49,9 +49,9 @@ fired = true; | ||
function parallel(ar, processor, cb) { | ||
// TODO: limit parallelization? | ||
// TODO: limit parallelization? | ||
if (ar.length === 0) | ||
return void cb(null, []) | ||
var left = ar.length; | ||
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) { | ||
var processorCb = function (idx, err, result) { | ||
if (err) { | ||
@@ -69,3 +69,3 @@ if (!failed) { | ||
ar.forEach(function (value, idx) { | ||
processor(value, processorCb.bind(null, idx)); | ||
processor(value, processorCb.bind(null, idx), idx); | ||
}); | ||
@@ -93,3 +93,3 @@ } | ||
function isIdentifierPropSchema(propSchema) { | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
} | ||
@@ -122,2 +122,15 @@ | ||
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 propSchema | ||
} | ||
/** | ||
@@ -212,6 +225,7 @@ * Returns the standard model schema associated with a class / constructor function | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function primitive() { | ||
return { | ||
function primitive(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
@@ -226,3 +240,5 @@ invariant(isPrimitive(value), "this value is not primitive: " + value); | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -444,2 +460,4 @@ | ||
var rootContextCache = new Map(); | ||
function Context(parentContext, modelSchema, json, onReadyCb, customArgs) { | ||
@@ -452,3 +470,3 @@ this.parentContext = parentContext; | ||
this.json = json; | ||
this.target = null; | ||
this.target = null; // always set this property using setTarget | ||
this.hasError = false; | ||
@@ -470,3 +488,3 @@ this.modelSchema = modelSchema; | ||
// once: defend against user-land calling 'done' twice | ||
return once(function(err, value) { | ||
return once(function (err, value) { | ||
if (err) { | ||
@@ -476,2 +494,3 @@ if (!this.hasError) { | ||
this.onReadyCb(err); | ||
rootContextCache.delete(this); | ||
} | ||
@@ -481,13 +500,16 @@ } else if (!this.hasError) { | ||
if (--this.pendingCallbacks === this.pendingRefsCount) { | ||
if (this.pendingRefsCount > 0) | ||
// all pending callbacks are pending reference resolvers. not good. | ||
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("\", \"") + | ||
"\"" | ||
)); | ||
else | ||
this.onReadyCb(null, this.target); | ||
"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); | ||
} | ||
} | ||
@@ -520,3 +542,3 @@ } | ||
// given a model schema, uuid and value, resolve all references that where looking for this object | ||
Context.prototype.resolve = function(modelSchema, uuid, value) { | ||
Context.prototype.resolve = function (modelSchema, uuid, value) { | ||
invariant(this.isRoot); | ||
@@ -540,5 +562,47 @@ if (!this.resolvedRefs[uuid]) | ||
// set target and update root context cache | ||
Context.prototype.setTarget = function (target) { | ||
if (this.isRoot && this.target) { | ||
rootContextCache.delete(this.target); | ||
} | ||
this.target = target; | ||
rootContextCache.set(this.target, this); | ||
}; | ||
// call all remaining reference lookup callbacks indicating an error during ref resolution | ||
Context.prototype.cancelAwaits = function () { | ||
invariant(this.isRoot); | ||
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; | ||
}; | ||
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 | ||
*/ | ||
function schemaHasAlias(schema, name) { | ||
@@ -554,4 +618,7 @@ for (var key in schema.props) | ||
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); | ||
// 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; | ||
@@ -562,3 +629,4 @@ } | ||
/** | ||
* Deserializes a json structor into an object graph. | ||
* Deserializes a json structure into an object graph. | ||
* | ||
* This process might be asynchronous (for example if there are references with an asynchronous | ||
@@ -570,5 +638,8 @@ * lookup function). The function returns an object (or array of objects), but the returned object | ||
* @param {json} json data to deserialize | ||
* @param {function} callback node style callback that is invoked once the deserializaiton 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. | ||
* @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. | ||
@@ -593,16 +664,18 @@ */ | ||
} else | ||
return deserializeObjectWithSchema(null, schema, json, callback, customArgs) | ||
return deserializeObjectWithSchema(null, schema, json, callback, customArgs) | ||
} | ||
function deserializeObjectWithSchema(parentContext, schema, json, callback, customArgs) { | ||
if (json === null || json === undefined) | ||
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, schema, json, callback, customArgs); | ||
var target = schema.factory(context); | ||
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.target = target; | ||
// 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, schema, json, target); | ||
deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
@@ -612,10 +685,59 @@ return target | ||
function deserializePropsWithSchema(context, schema, json, target) { | ||
if (schema.extends) | ||
deserializePropsWithSchema(context, schema.extends, json, target); | ||
Object.keys(schema.props).forEach(function (propName) { | ||
var propDef = schema.props[propName]; | ||
function deserializePropsWithSchema(context, modelSchema, json, target) { | ||
if (modelSchema.extends) | ||
deserializePropsWithSchema(context, modelSchema.extends, json, target); | ||
function deserializeProp(propDef, jsonValue, propName) { | ||
function setValue(value) { | ||
if (value !== SKIP) { | ||
target[propName] = value; | ||
} | ||
} | ||
function preProcess(resultCallback) { | ||
return function (err, newValue) { | ||
function finalCallback(errPreliminary, finalOrRetryValue) { | ||
if (errPreliminary && finalOrRetryValue !== undefined && | ||
typeof propDef.afterDeserialize === "function") { | ||
propDef.deserializer( | ||
finalOrRetryValue, | ||
preProcess(resultCallback), | ||
context, | ||
target[propName] | ||
); | ||
} else { | ||
resultCallback(errPreliminary, finalOrRetryValue); | ||
} | ||
} | ||
onAfterDeserialize(finalCallback, err, newValue, jsonValue, json, | ||
propName, context, propDef); | ||
} | ||
} | ||
propDef.deserializer( | ||
jsonValue, | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
preProcess(context.rootContext.createCallback(setValue)), | ||
context, | ||
target[propName] // initial value | ||
); | ||
} | ||
Object.keys(modelSchema.props).forEach(function (propName) { | ||
var propDef = modelSchema.props[propName]; | ||
function callbackDeserialize(err, jsonValue) { | ||
if (!err && jsonValue !== undefined) { | ||
deserializeProp(propDef, jsonValue, propName); | ||
} | ||
} | ||
if (propName === "*") { | ||
invariant(propDef === true, "prop schema '*' can onle be used with 'true'"); | ||
deserializeStarProps(schema, target, json); | ||
invariant(propDef === true, "prop schema '*' can only be used with 'true'"); | ||
deserializeStarProps(modelSchema, target, json); | ||
return | ||
@@ -628,20 +750,30 @@ } | ||
var jsonAttr = propDef.jsonname || propName; | ||
if (!(jsonAttr in json)) | ||
return | ||
propDef.deserializer( | ||
json[jsonAttr], | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
context.rootContext.createCallback(function (value) { | ||
if (value !== SKIP){ | ||
target[propName] = value; | ||
} | ||
}), | ||
context, | ||
target[propName] // initial value | ||
); | ||
var jsonValue = json[jsonAttr]; | ||
onBeforeDeserialize(callbackDeserialize, jsonValue, json, jsonAttr, 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); | ||
} | ||
} | ||
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); | ||
} | ||
} | ||
/* | ||
@@ -661,2 +793,3 @@ * Update | ||
* @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. | ||
*/ | ||
@@ -680,6 +813,7 @@ function update(modelSchema, target, json, callback, customArgs) { | ||
var context = new Context(null, modelSchema, json, callback, customArgs); | ||
context.target = target; | ||
context.setTarget(target); | ||
var lock = context.createCallback(GUARDED_NOOP); | ||
deserializePropsWithSchema(context, modelSchema, json, target); | ||
var result = deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
return result | ||
} | ||
@@ -704,3 +838,3 @@ | ||
* var todos = {}; | ||
* | ||
* | ||
* var s = _.createSimpleSchema({ | ||
@@ -710,3 +844,3 @@ * id: _.identifier((id, object) => (todos[id] = object)), | ||
* }); | ||
* | ||
* | ||
* _.deserialize(s, { | ||
@@ -717,3 +851,3 @@ * id: 1, | ||
* _.deserialize(s, [{ id: 2, title: 'test2' }, { id: 1, title: 'test1' }]); | ||
* | ||
* | ||
* t.deepEqual(todos, { | ||
@@ -724,9 +858,17 @@ * 1: { id: 1, title: 'test1' }, | ||
* | ||
* @param {RegisterFunction} registerFn optional function to register this object during creation. | ||
* @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(registerFn) { | ||
invariant(!registerFn || typeof registerFn === "function", "First argument should be omitted or function"); | ||
return { | ||
function identifier(arg1, arg2) { | ||
var registerFn, additionalArgs; | ||
if (typeof arg1 === "function") { | ||
registerFn = arg1; | ||
additionalArgs = arg2; | ||
} else { | ||
additionalArgs = arg1; | ||
} | ||
invariant(!additionalArgs || typeof additionalArgs === "object", "Additional property arguments should be an object, register function should be omitted or a funtion"); | ||
var result = { | ||
identifier: true, | ||
@@ -742,3 +884,5 @@ serializer: _defaultPrimitiveProp.serializer, | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -749,7 +893,8 @@ | ||
* | ||
* @returns | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function date() { | ||
function date(additionalArgs) { | ||
// TODO: add format option? | ||
return { | ||
var result = { | ||
serializer: function(value) { | ||
@@ -766,3 +911,5 @@ if (value === null || value === undefined) | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -795,3 +942,5 @@ | ||
deserializer: propSchema.deserializer, | ||
identifier: isIdentifierPropSchema(propSchema) | ||
identifier: isIdentifierPropSchema(propSchema), | ||
beforeDeserialize: propSchema.beforeDeserialize, | ||
afterDeserialize: propSchema.afterDeserialize | ||
} | ||
@@ -854,17 +1003,20 @@ } | ||
* @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) { | ||
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"); | ||
return { | ||
var result = { | ||
serializer: serializer, | ||
deserializer: function (jsonValue, done, context, oldValue) { | ||
if (deserializer.length === 4) { | ||
deserializer(jsonValue, context, oldValue, done); | ||
deserializer(jsonValue, context, oldValue, done, additionalArgs); | ||
} else { | ||
done(null, deserializer(jsonValue, context, oldValue)); | ||
done(null, deserializer(jsonValue, context, oldValue, null, additionalArgs)); | ||
} | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -898,7 +1050,8 @@ | ||
* @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) { | ||
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."); | ||
return { | ||
var result = { | ||
serializer: function (item) { | ||
@@ -916,5 +1069,7 @@ modelSchema = getDefaultModelSchema(modelSchema); | ||
return void done(null, childJson) | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done) | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done, additionalArgs) | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -980,13 +1135,18 @@ | ||
* @param target: ModelSchema or string | ||
* @param {RefLookupFunction} lookupFn function | ||
* @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) { | ||
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, "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"); | ||
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") | ||
@@ -1002,3 +1162,3 @@ childIdentifierAttribute = target; | ||
} | ||
return { | ||
var result = { | ||
serializer: function (item) { | ||
@@ -1017,3 +1177,5 @@ if (!initialized) | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -1048,9 +1210,11 @@ | ||
* @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) { | ||
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"); | ||
return { | ||
invariant(!isAliasedPropSchema(propSchema), | ||
"provided prop is aliased, please put aliases first"); | ||
var result = { | ||
serializer: function (ar) { | ||
@@ -1060,14 +1224,51 @@ invariant(ar && "length" in ar && "map" in ar, "expected array (like) object"); | ||
}, | ||
deserializer: function(jsonArray, done, context) { | ||
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 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") { | ||
propSchema.deserializer( | ||
finalOrRetryValue, | ||
deserializeDone, | ||
context | ||
); | ||
} else { | ||
onItemDone(errPreliminary, finalOrRetryValue); | ||
} | ||
} | ||
onBeforeDeserialize(callbackBefore, jsonValue, jsonArray, itemIndex, context, | ||
propSchema); | ||
} | ||
parallel( | ||
jsonArray, | ||
function (item, itemDone) { | ||
return propSchema.deserializer(item, itemDone, context) | ||
}, | ||
processItem, | ||
done | ||
); | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -1081,9 +1282,10 @@ | ||
* @param {*} propSchema | ||
* @returns | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function map(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"); | ||
return { | ||
var res = { | ||
serializer: function (m) { | ||
@@ -1105,3 +1307,3 @@ invariant(m && typeof m === "object", "expected object or Map"); | ||
var keys = Object.keys(jsonObject); | ||
list(propSchema).deserializer( | ||
list(propSchema, additionalArgs).deserializer( | ||
keys.map(function (key) { | ||
@@ -1133,52 +1335,68 @@ return jsonObject[key] | ||
} | ||
} | ||
}; | ||
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. | ||
* 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, {string} keyPropertyName - the property of stored objects used as key in the map | ||
* @returns | ||
* @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) { | ||
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"); | ||
return { | ||
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 | ||
m.forEach(function (value, key) { | ||
result.push(propSchema.serializer(value)); | ||
}); | ||
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).deserializer( | ||
jsonArray, | ||
function (err, values) { | ||
if (err) | ||
return void done(err) | ||
var isMap = isMapLike(oldValue); | ||
var newValue; | ||
if (isMap) { | ||
oldValue.clear(); | ||
newValue = oldValue; | ||
} else | ||
newValue = {}; | ||
for (var i = 0, l = jsonArray.length; i < l; i++) | ||
if (isMap) | ||
newValue.set(values[i][keyPropertyName], values[i]); | ||
else | ||
newValue[values[i][keyPropertyName].toString()] = values[i]; | ||
done(null, newValue); | ||
}, | ||
context | ||
); | ||
list(propSchema, additionalArgs).deserializer( | ||
jsonArray, | ||
function (err, values) { | ||
if (err) | ||
return void done(err) | ||
var 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 | ||
} | ||
@@ -1198,6 +1416,7 @@ | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function raw() { | ||
return { | ||
function raw(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
@@ -1209,3 +1428,5 @@ return value | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -1267,2 +1488,2 @@ | ||
export { createSimpleSchema, createModelSchema, getDefaultModelSchema, setDefaultModelSchema, serializable, serialize, serializeAll, 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, reference, list, map, mapAsArray, raw, SKIP, object as child, reference as ref }; |
@@ -40,3 +40,3 @@ /** serializr - (c) Michel Weststrate 2016 - MIT Licensed */ | ||
var fired = false; | ||
return function() { | ||
return function () { | ||
if (!fired) { | ||
@@ -56,9 +56,9 @@ fired = true; | ||
function parallel(ar, processor, cb) { | ||
// TODO: limit parallelization? | ||
// TODO: limit parallelization? | ||
if (ar.length === 0) | ||
return void cb(null, []) | ||
var left = ar.length; | ||
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) { | ||
var processorCb = function (idx, err, result) { | ||
if (err) { | ||
@@ -76,3 +76,3 @@ if (!failed) { | ||
ar.forEach(function (value, idx) { | ||
processor(value, processorCb.bind(null, idx)); | ||
processor(value, processorCb.bind(null, idx), idx); | ||
}); | ||
@@ -100,3 +100,3 @@ } | ||
function isIdentifierPropSchema(propSchema) { | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
} | ||
@@ -129,2 +129,15 @@ | ||
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 propSchema | ||
} | ||
/** | ||
@@ -219,6 +232,7 @@ * Returns the standard model schema associated with a class / constructor function | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function primitive() { | ||
return { | ||
function primitive(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
@@ -233,3 +247,5 @@ invariant(isPrimitive(value), "this value is not primitive: " + value); | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -451,2 +467,4 @@ | ||
var rootContextCache = new Map(); | ||
function Context(parentContext, modelSchema, json, onReadyCb, customArgs) { | ||
@@ -459,3 +477,3 @@ this.parentContext = parentContext; | ||
this.json = json; | ||
this.target = null; | ||
this.target = null; // always set this property using setTarget | ||
this.hasError = false; | ||
@@ -477,3 +495,3 @@ this.modelSchema = modelSchema; | ||
// once: defend against user-land calling 'done' twice | ||
return once(function(err, value) { | ||
return once(function (err, value) { | ||
if (err) { | ||
@@ -483,2 +501,3 @@ if (!this.hasError) { | ||
this.onReadyCb(err); | ||
rootContextCache.delete(this); | ||
} | ||
@@ -488,13 +507,16 @@ } else if (!this.hasError) { | ||
if (--this.pendingCallbacks === this.pendingRefsCount) { | ||
if (this.pendingRefsCount > 0) | ||
// all pending callbacks are pending reference resolvers. not good. | ||
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("\", \"") + | ||
"\"" | ||
)); | ||
else | ||
this.onReadyCb(null, this.target); | ||
"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); | ||
} | ||
} | ||
@@ -527,3 +549,3 @@ } | ||
// given a model schema, uuid and value, resolve all references that where looking for this object | ||
Context.prototype.resolve = function(modelSchema, uuid, value) { | ||
Context.prototype.resolve = function (modelSchema, uuid, value) { | ||
invariant(this.isRoot); | ||
@@ -547,5 +569,47 @@ if (!this.resolvedRefs[uuid]) | ||
// set target and update root context cache | ||
Context.prototype.setTarget = function (target) { | ||
if (this.isRoot && this.target) { | ||
rootContextCache.delete(this.target); | ||
} | ||
this.target = target; | ||
rootContextCache.set(this.target, this); | ||
}; | ||
// call all remaining reference lookup callbacks indicating an error during ref resolution | ||
Context.prototype.cancelAwaits = function () { | ||
invariant(this.isRoot); | ||
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; | ||
}; | ||
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 | ||
*/ | ||
function schemaHasAlias(schema, name) { | ||
@@ -561,4 +625,7 @@ for (var key in schema.props) | ||
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); | ||
// 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; | ||
@@ -569,3 +636,4 @@ } | ||
/** | ||
* Deserializes a json structor into an object graph. | ||
* Deserializes a json structure into an object graph. | ||
* | ||
* This process might be asynchronous (for example if there are references with an asynchronous | ||
@@ -577,5 +645,8 @@ * lookup function). The function returns an object (or array of objects), but the returned object | ||
* @param {json} json data to deserialize | ||
* @param {function} callback node style callback that is invoked once the deserializaiton 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. | ||
* @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. | ||
@@ -600,16 +671,18 @@ */ | ||
} else | ||
return deserializeObjectWithSchema(null, schema, json, callback, customArgs) | ||
return deserializeObjectWithSchema(null, schema, json, callback, customArgs) | ||
} | ||
function deserializeObjectWithSchema(parentContext, schema, json, callback, customArgs) { | ||
if (json === null || json === undefined) | ||
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, schema, json, callback, customArgs); | ||
var target = schema.factory(context); | ||
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.target = target; | ||
// 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, schema, json, target); | ||
deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
@@ -619,10 +692,59 @@ return target | ||
function deserializePropsWithSchema(context, schema, json, target) { | ||
if (schema.extends) | ||
deserializePropsWithSchema(context, schema.extends, json, target); | ||
Object.keys(schema.props).forEach(function (propName) { | ||
var propDef = schema.props[propName]; | ||
function deserializePropsWithSchema(context, modelSchema, json, target) { | ||
if (modelSchema.extends) | ||
deserializePropsWithSchema(context, modelSchema.extends, json, target); | ||
function deserializeProp(propDef, jsonValue, propName) { | ||
function setValue(value) { | ||
if (value !== SKIP) { | ||
target[propName] = value; | ||
} | ||
} | ||
function preProcess(resultCallback) { | ||
return function (err, newValue) { | ||
function finalCallback(errPreliminary, finalOrRetryValue) { | ||
if (errPreliminary && finalOrRetryValue !== undefined && | ||
typeof propDef.afterDeserialize === "function") { | ||
propDef.deserializer( | ||
finalOrRetryValue, | ||
preProcess(resultCallback), | ||
context, | ||
target[propName] | ||
); | ||
} else { | ||
resultCallback(errPreliminary, finalOrRetryValue); | ||
} | ||
} | ||
onAfterDeserialize(finalCallback, err, newValue, jsonValue, json, | ||
propName, context, propDef); | ||
} | ||
} | ||
propDef.deserializer( | ||
jsonValue, | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
preProcess(context.rootContext.createCallback(setValue)), | ||
context, | ||
target[propName] // initial value | ||
); | ||
} | ||
Object.keys(modelSchema.props).forEach(function (propName) { | ||
var propDef = modelSchema.props[propName]; | ||
function callbackDeserialize(err, jsonValue) { | ||
if (!err && jsonValue !== undefined) { | ||
deserializeProp(propDef, jsonValue, propName); | ||
} | ||
} | ||
if (propName === "*") { | ||
invariant(propDef === true, "prop schema '*' can onle be used with 'true'"); | ||
deserializeStarProps(schema, target, json); | ||
invariant(propDef === true, "prop schema '*' can only be used with 'true'"); | ||
deserializeStarProps(modelSchema, target, json); | ||
return | ||
@@ -635,20 +757,30 @@ } | ||
var jsonAttr = propDef.jsonname || propName; | ||
if (!(jsonAttr in json)) | ||
return | ||
propDef.deserializer( | ||
json[jsonAttr], | ||
// for individual props, use root context based callbacks | ||
// this allows props to complete after completing the object itself | ||
// enabling reference resolving and such | ||
context.rootContext.createCallback(function (value) { | ||
if (value !== SKIP){ | ||
target[propName] = value; | ||
} | ||
}), | ||
context, | ||
target[propName] // initial value | ||
); | ||
var jsonValue = json[jsonAttr]; | ||
onBeforeDeserialize(callbackDeserialize, jsonValue, json, jsonAttr, 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); | ||
} | ||
} | ||
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); | ||
} | ||
} | ||
/* | ||
@@ -668,2 +800,3 @@ * Update | ||
* @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. | ||
*/ | ||
@@ -687,6 +820,7 @@ function update(modelSchema, target, json, callback, customArgs) { | ||
var context = new Context(null, modelSchema, json, callback, customArgs); | ||
context.target = target; | ||
context.setTarget(target); | ||
var lock = context.createCallback(GUARDED_NOOP); | ||
deserializePropsWithSchema(context, modelSchema, json, target); | ||
var result = deserializePropsWithSchema(context, modelSchema, json, target); | ||
lock(); | ||
return result | ||
} | ||
@@ -711,3 +845,3 @@ | ||
* var todos = {}; | ||
* | ||
* | ||
* var s = _.createSimpleSchema({ | ||
@@ -717,3 +851,3 @@ * id: _.identifier((id, object) => (todos[id] = object)), | ||
* }); | ||
* | ||
* | ||
* _.deserialize(s, { | ||
@@ -724,3 +858,3 @@ * id: 1, | ||
* _.deserialize(s, [{ id: 2, title: 'test2' }, { id: 1, title: 'test1' }]); | ||
* | ||
* | ||
* t.deepEqual(todos, { | ||
@@ -731,9 +865,17 @@ * 1: { id: 1, title: 'test1' }, | ||
* | ||
* @param {RegisterFunction} registerFn optional function to register this object during creation. | ||
* @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(registerFn) { | ||
invariant(!registerFn || typeof registerFn === "function", "First argument should be omitted or function"); | ||
return { | ||
function identifier(arg1, arg2) { | ||
var registerFn, additionalArgs; | ||
if (typeof arg1 === "function") { | ||
registerFn = arg1; | ||
additionalArgs = arg2; | ||
} else { | ||
additionalArgs = arg1; | ||
} | ||
invariant(!additionalArgs || typeof additionalArgs === "object", "Additional property arguments should be an object, register function should be omitted or a funtion"); | ||
var result = { | ||
identifier: true, | ||
@@ -749,3 +891,5 @@ serializer: _defaultPrimitiveProp.serializer, | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -756,7 +900,8 @@ | ||
* | ||
* @returns | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function date() { | ||
function date(additionalArgs) { | ||
// TODO: add format option? | ||
return { | ||
var result = { | ||
serializer: function(value) { | ||
@@ -773,3 +918,5 @@ if (value === null || value === undefined) | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -802,3 +949,5 @@ | ||
deserializer: propSchema.deserializer, | ||
identifier: isIdentifierPropSchema(propSchema) | ||
identifier: isIdentifierPropSchema(propSchema), | ||
beforeDeserialize: propSchema.beforeDeserialize, | ||
afterDeserialize: propSchema.afterDeserialize | ||
} | ||
@@ -861,17 +1010,20 @@ } | ||
* @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) { | ||
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"); | ||
return { | ||
var result = { | ||
serializer: serializer, | ||
deserializer: function (jsonValue, done, context, oldValue) { | ||
if (deserializer.length === 4) { | ||
deserializer(jsonValue, context, oldValue, done); | ||
deserializer(jsonValue, context, oldValue, done, additionalArgs); | ||
} else { | ||
done(null, deserializer(jsonValue, context, oldValue)); | ||
done(null, deserializer(jsonValue, context, oldValue, null, additionalArgs)); | ||
} | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -905,7 +1057,8 @@ | ||
* @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) { | ||
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."); | ||
return { | ||
var result = { | ||
serializer: function (item) { | ||
@@ -923,5 +1076,7 @@ modelSchema = getDefaultModelSchema(modelSchema); | ||
return void done(null, childJson) | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done) | ||
return void deserializeObjectWithSchema(context, modelSchema, childJson, done, additionalArgs) | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -987,13 +1142,18 @@ | ||
* @param target: ModelSchema or string | ||
* @param {RefLookupFunction} lookupFn function | ||
* @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) { | ||
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, "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"); | ||
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") | ||
@@ -1009,3 +1169,3 @@ childIdentifierAttribute = target; | ||
} | ||
return { | ||
var result = { | ||
serializer: function (item) { | ||
@@ -1024,3 +1184,5 @@ if (!initialized) | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -1055,9 +1217,11 @@ | ||
* @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) { | ||
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"); | ||
return { | ||
invariant(!isAliasedPropSchema(propSchema), | ||
"provided prop is aliased, please put aliases first"); | ||
var result = { | ||
serializer: function (ar) { | ||
@@ -1067,14 +1231,51 @@ invariant(ar && "length" in ar && "map" in ar, "expected array (like) object"); | ||
}, | ||
deserializer: function(jsonArray, done, context) { | ||
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 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") { | ||
propSchema.deserializer( | ||
finalOrRetryValue, | ||
deserializeDone, | ||
context | ||
); | ||
} else { | ||
onItemDone(errPreliminary, finalOrRetryValue); | ||
} | ||
} | ||
onBeforeDeserialize(callbackBefore, jsonValue, jsonArray, itemIndex, context, | ||
propSchema); | ||
} | ||
parallel( | ||
jsonArray, | ||
function (item, itemDone) { | ||
return propSchema.deserializer(item, itemDone, context) | ||
}, | ||
processItem, | ||
done | ||
); | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -1088,9 +1289,10 @@ | ||
* @param {*} propSchema | ||
* @returns | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {PropSchema} | ||
*/ | ||
function map(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"); | ||
return { | ||
var res = { | ||
serializer: function (m) { | ||
@@ -1112,3 +1314,3 @@ invariant(m && typeof m === "object", "expected object or Map"); | ||
var keys = Object.keys(jsonObject); | ||
list(propSchema).deserializer( | ||
list(propSchema, additionalArgs).deserializer( | ||
keys.map(function (key) { | ||
@@ -1140,52 +1342,68 @@ return jsonObject[key] | ||
} | ||
} | ||
}; | ||
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. | ||
* 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, {string} keyPropertyName - the property of stored objects used as key in the map | ||
* @returns | ||
* @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) { | ||
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"); | ||
return { | ||
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 | ||
m.forEach(function (value, key) { | ||
result.push(propSchema.serializer(value)); | ||
}); | ||
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).deserializer( | ||
jsonArray, | ||
function (err, values) { | ||
if (err) | ||
return void done(err) | ||
var isMap = isMapLike(oldValue); | ||
var newValue; | ||
if (isMap) { | ||
oldValue.clear(); | ||
newValue = oldValue; | ||
} else | ||
newValue = {}; | ||
for (var i = 0, l = jsonArray.length; i < l; i++) | ||
if (isMap) | ||
newValue.set(values[i][keyPropertyName], values[i]); | ||
else | ||
newValue[values[i][keyPropertyName].toString()] = values[i]; | ||
done(null, newValue); | ||
}, | ||
context | ||
); | ||
list(propSchema, additionalArgs).deserializer( | ||
jsonArray, | ||
function (err, values) { | ||
if (err) | ||
return void done(err) | ||
var 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 | ||
} | ||
@@ -1205,6 +1423,7 @@ | ||
* | ||
* @param {AdditionalPropArgs} additionalArgs optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
* @returns {ModelSchema} | ||
*/ | ||
function raw() { | ||
return { | ||
function raw(additionalArgs) { | ||
var result = { | ||
serializer: function (value) { | ||
@@ -1216,3 +1435,5 @@ return value | ||
} | ||
} | ||
}; | ||
result = processAdditionalPropArgs(result, additionalArgs); | ||
return result | ||
} | ||
@@ -1281,2 +1502,3 @@ | ||
exports.serializeAll = serializeAll; | ||
exports.cancelDeserialize = cancelDeserialize; | ||
exports.deserialize = deserialize; | ||
@@ -1283,0 +1505,0 @@ exports.update = update; |
/** serializr - (c) Michel Weststrate 2016 - MIT Licensed */ | ||
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define("serializr",["exports"],r):r(e.serializr={})}(this,function(e){"use strict";function r(e){if(e)throw new Error(e)}function n(e,r){if(!e)throw new Error("[serializr] "+(r||"Illegal State"))}function t(e,r,n){if(0!==e.length){var t=e.length,i=[],o=!1;e.forEach(function(e,a){r(e,function(e,r,a){r?o||(o=!0,n(r)):(i[e]=a,0==--t&&n(null,i))}.bind(null,a))})}else n(null,[])}function i(e){return null===e||"object"!=typeof e&&"function"!=typeof e}function o(e){return e&&e.factory&&e.props}function a(e){return e&&e.serializer&&e.deserializer}function s(e){return"object"==typeof e&&!!e.jsonname}function u(e,r){for(;e;){if(e===r)return!0;e=e["extends"]}return!1}function l(e){return e&&"function"==typeof e.keys&&"function"==typeof e.clear}function c(e){return e?o(e)?e:o(e.serializeInfo)?e.serializeInfo:e.constructor&&e.constructor.serializeInfo?e.constructor.serializeInfo:void 0:null}function f(e,r){return n(o(r)),e.serializeInfo=r}function p(e,r,t){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:t||function(){return new e},props:r};if(e.prototype.constructor!==Object){var o=c(e.prototype.constructor);o&&o.targetClass!==e&&(i["extends"]=o)}return f(e,i),i}function d(){return{serializer:function(e){return n(i(e),"this value is not primitive: "+e),e},deserializer:function(e,r){i(e)?r(null,e):r("[serializr] this value is not primitive: "+e)}}}var h="undefined"!=typeof Symbol?Symbol("SKIP"):{SKIP:!0},m=d(),y=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,g=/([^\s,]+)/g;function v(e,r,t,i){var o,s,u;if(n(arguments.length>=2,"too few arguments. Please use @serializable as property decorator"),t===undefined&&"function"==typeof r&&r.prototype&&i!==undefined&&"number"==typeof i){n(a(e),"Constructor params must use alias(name)"),n(e.jsonname,"Constructor params must use alias(name)");var l=(s=r.toString().replace(y,""),null===(u=s.slice(s.indexOf("(")+1,s.indexOf(")")).match(g))&&(u=[]),u);l.length>=i&&(t=l[i],e.paramNumber=i,i=undefined,r=r.prototype,o=function(e){for(var n=[],t=0;t<r.constructor.length;t++)Object.keys(e.modelSchema.props).forEach(function(r){var i=e.modelSchema.props[r];i.paramNumber===t&&(n[t]=e.json[i.jsonname])});return new(Function.prototype.bind.apply(r.constructor,[null].concat(n)))})}n("string"==typeof t,"incorrect usage of @serializable decorator");var f=c(r);return f&&r.constructor.hasOwnProperty("serializeInfo")||(f=p(r.constructor,{},o)),f&&f.targetClass!==r.constructor&&(f=p(r.constructor,{},o)),f.props[t]=e,!i||i.get||i.set||(i.writable=!0),i}function b(e,r){n(1===arguments.length||2===arguments.length,"serialize expects one or 2 arguments");var t=1===arguments.length?e:r,i=1===arguments.length?null:e;if(Array.isArray(t)){if(0===t.length)return[];i||(i=c(t[0]))}else i||(i=c(t));return n(!!i,"Failed to find default schema for "+e),Array.isArray(t)?t.map(function(e){return z(i,e)}):z(i,t)}function z(e,r){var t;return n(e&&"object"==typeof e,"Expected schema"),n(r&&"object"==typeof r,"Expected object"),t=e["extends"]?z(e["extends"],r):{},Object.keys(e.props).forEach(function(o){var a=e.props[o];if("*"===o)return n(!0===a,"prop schema '*' can onle be used with 'true'"),void function(e,r,n){for(var t in r)if(r.hasOwnProperty(t)&&!(t in e.props)){var o=r[t];i(o)&&(n[t]=o)}}(e,r,t);if(!0===a&&(a=m),!1!==a){var s=a.serializer(r[o],o,r);s!==h&&(t[a.jsonname||o]=s)}}),t}function j(e,n,t,i,o){this.parentContext=e,this.isRoot=!e,this.pendingCallbacks=0,this.pendingRefsCount=0,this.onReadyCb=i||r,this.json=t,this.target=null,this.hasError=!1,this.modelSchema=n,this.isRoot?(this.rootContext=this,this.args=o,this.pendingRefs={},this.resolvedRefs={}):(this.rootContext=e.rootContext,this.args=e.args)}function x(e,r){for(var n in e.props)if("object"==typeof e.props[n]&&e.props[n].jsonname===r)return!0;return!1}function R(e,t,i,o,a){if(null!==i&&i!==undefined){var s=new j(e,t,i,o,a),u=t.factory(s);n(!!u,"No object returned from factory"),s.target=u;var l=s.createCallback(r);return C(s,t,i,u),l(),u}o(null,null)}function C(e,r,t,o){r["extends"]&&C(e,r["extends"],t,o),Object.keys(r.props).forEach(function(a){var s=r.props[a];if("*"===a)return n(!0===s,"prop schema '*' can onle be used with 'true'"),void function(e,r,t){for(var o in t)if(!(o in e.props||x(e,o))){var a=t[o];n(i(a),"encountered non primitive value while deserializing '*' properties in property '"+o+"': "+a),r[o]=a}}(r,o,t);if(!0===s&&(s=m),!1!==s){var u=s.jsonname||a;u in t&&s.deserializer(t[u],e.rootContext.createCallback(function(e){e!==h&&(o[a]=e)}),e,o[a])}})}function S(e){return n("object"==typeof e||"function"==typeof e,"No modelschema provided. If you are importing it from another file be aware of circular dependencies."),{serializer:function(r){return n(o(e=c(e)),"expected modelSchema, got "+e),null===r||r===undefined?r:b(e,r)},deserializer:function(r,t,i){n(o(e=c(e)),"expected modelSchema, got "+e),null!==r&&r!==undefined?R(i,e,r,t):t(null,r)}}}function w(e,r){n(!!e,"No modelschema provided. If you are importing it from another file be aware of circular dependencies.");var t,i=!1;function a(){if(i=!0,n("string"!=typeof e||r,"if the reference target is specified by attribute name, a lookup function is required"),n(!r||"function"==typeof r,"second argument should be a lookup function"),"string"==typeof e)t=e;else{var a=c(e);n(o(a),"expected model schema or string as first argument for 'ref', got "+a),r=r||(s=a,function(e,r,n){n.rootContext.await(s,e,r)}),n(!!(t=function(e){for(n(o(e));e;){for(var r in e.props)if("object"==typeof e.props[r]&&!0===e.props[r].identifier)return r;e=e["extends"]}return null}(a)),"provided model schema doesn't define an identifier() property and cannot be used by 'ref'.")}var s}return{serializer:function(e){return i||a(),e?e[t]:null},deserializer:function(e,n,t){i||a(),null===e||e===undefined?n(null,e):r(e,n,t)}}}function k(e){return n(a(e=e||m),"expected prop schema as first argument"),n(!s(e),"provided prop is aliased, please put aliases first"),{serializer:function(r){return n(r&&"length"in r&&"map"in r,"expected array (like) object"),r.map(e.serializer)},deserializer:function(r,n,i){Array.isArray(r)?t(r,function(r,n){return e.deserializer(r,n,i)},n):n("[serializr] expected JSON array")}}}j.prototype.createCallback=function(e){return this.pendingCallbacks++,r=function(r,n){r?this.hasError||(this.hasError=!0,this.onReadyCb(r)):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('", "')+'"')):this.onReadyCb(null,this.target)))}.bind(this),t=!1,function(){if(!t)return t=!0,r.apply(null,arguments);n(!1,"callback was invoked twice")};var r,t},j.prototype.await=function(e,r,t){if(n(this.isRoot),r in this.resolvedRefs){var i=this.resolvedRefs[r].filter(function(r){return u(r.modelSchema,e)})[0];if(i)return void t(null,i.value)}this.pendingRefsCount++,this.pendingRefs[r]||(this.pendingRefs[r]=[]),this.pendingRefs[r].push({modelSchema:e,uuid:r,callback:t})},j.prototype.resolve=function(e,r,t){if(n(this.isRoot),this.resolvedRefs[r]||(this.resolvedRefs[r]=[]),this.resolvedRefs[r].push({modelSchema:e,value:t}),r in this.pendingRefs)for(var i=this.pendingRefs[r].length-1;i>=0;i--){var o=this.pendingRefs[r][i];u(e,o.modelSchema)&&(this.pendingRefs[r].splice(i,1),this.pendingRefsCount--,o.callback(null,t))}},e.createSimpleSchema=function(e){return{factory:function(){return{}},props:e}},e.createModelSchema=p,e.getDefaultModelSchema=c,e.setDefaultModelSchema=f,e.serializable=function(e,r,t){if(1===arguments.length){var i=!0===e?m:e;return n(a(i),"@serializable expects prop schema"),v.bind(null,i)}return v(d(),e,r,t)},e.serialize=b,e.serializeAll=function(e){n(1===arguments.length&&"function"==typeof e,"@serializeAll can only be used as class decorator");var r=c(e);return r&&e.hasOwnProperty("serializeInfo")||f(e,r=p(e,{})),c(e).props["*"]=!0,e},e.deserialize=function(e,i,a,s){if(n(arguments.length>=2,"deserialize expects at least 2 arguments"),n(o(e=c(e)),"first argument should be model schema"),Array.isArray(i)){var u=[];return t(i,function(r,n){var t=R(null,e,r,n,s);u.push(t)},a||r),u}return R(null,e,i,a,s)},e.update=function(e,t,i,a,s){2===arguments.length||"function"==typeof arguments[2]?(e=c(t=arguments[0]),i=arguments[1],a=arguments[2],s=arguments[3]):e=c(e),n(o(e),"update failed to determine schema"),n("object"==typeof t&&t&&!Array.isArray(t),"update needs an object");var u=new j(null,e,i,a,s);u.target=t;var l=u.createCallback(r);C(u,e,i,t),l()},e.primitive=d,e.identifier=function(e){return n(!e||"function"==typeof e,"First argument should be omitted or function"),{identifier:!0,serializer:m.serializer,deserializer:function(r,n,t){m.deserializer(r,function(r,i){var o,a;o=i,t.target,(a=t).rootContext.resolve(a.modelSchema,o,a.target),e&&e(i,t.target,t),n(r,i)})}}},e.date=function(){return{serializer:function(e){return null===e||e===undefined?e:(n(e instanceof Date,"Expected Date object"),e.getTime())},deserializer:function(e,r){null!==e&&e!==undefined?r(null,new Date(e)):r(null,e)}}},e.alias=function(e,r){return n(e&&"string"==typeof e,"expected prop name as first argument"),n(a(r=r&&!0!==r?r:m),"expected prop schema as second argument"),n(!s(r),"provided prop is already aliased"),{jsonname:e,serializer:r.serializer,deserializer:r.deserializer,identifier:(t=r,"object"==typeof t&&!0===t.identifier)};var t},e.custom=function(e,r){return n("function"==typeof e,"first argument should be function"),n("function"==typeof r,"second argument should be a function or promise"),{serializer:e,deserializer:function(e,n,t,i){4===r.length?r(e,t,i,n):n(null,r(e,t,i))}}},e.object=S,e.reference=w,e.list=k,e.map=function(e){return n(a(e=e||m),"expected prop schema as first argument"),n(!s(e),"provided prop is aliased, please put aliases first"),{serializer:function(r){n(r&&"object"==typeof r,"expected object or Map");var t=l(r),i={};if(t)r.forEach(function(r,n){i[n]=e.serializer(r)});else for(var o in r)i[o]=e.serializer(r[o]);return i},deserializer:function(r,n,t,i){if(r&&"object"==typeof r){var o=Object.keys(r);k(e).deserializer(o.map(function(e){return r[e]}),function(e,r){if(e)n(e);else{var t,a=l(i);a?(i.clear(),t=i):t={};for(var s=0,u=o.length;s<u;s++)a?t.set(o[s],r[s]):t[o[s]]=r[s];n(null,t)}},t)}else n("[serializr] expected JSON object")}}},e.mapAsArray=function(e,r){return n(a(e=e||m),"expected prop schema as first argument"),n(!!r,"expected key property name as second argument"),{serializer:function(r){var n=[];return r.forEach(function(r,t){n.push(e.serializer(r))}),n},deserializer:function(n,t,i,o){k(e).deserializer(n,function(e,i){if(e)t(e);else{var a,s=l(o);s?(o.clear(),a=o):a={};for(var u=0,c=n.length;u<c;u++)s?a.set(i[u][r],i[u]):a[i[u][r].toString()]=i[u];t(null,a)}},i)}}},e.raw=function(){return{serializer:function(e){return e},deserializer:function(e,r){r(null,e)}}},e.SKIP=h,e.child=S,e.ref=w,Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!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})}); | ||
//# sourceMappingURL=serializr.min.js.map |
{ | ||
"name": "serializr", | ||
"version": "1.3.0", | ||
"version": "1.4.1", | ||
"description": "Serialize and deserialize complex object graphs to JSON", | ||
@@ -5,0 +5,0 @@ "main": "lib/serializr.js", |
188
README.md
@@ -260,2 +260,3 @@ # Serializr | ||
- `map(propSchema)`: Serializes an Map or string key based collection | ||
- `mapAsArray(propSchema, keyPropertyName)`: Serializes a map to an array of elements | ||
- `object(modelSchema)`: Serializes an child model element | ||
@@ -293,2 +294,42 @@ - `reference(modelSchema, lookupFunction?)`: Serializes a reference to another model element | ||
## AdditionalPropArgs | ||
A PropSchema can be further parameterized using AdditionalPropArgs. Currently, they can be used to specify lifecycle functions. During deserialization they can be useful, e.g. in case you want to | ||
- react to errors in the deserialization on a value level and retry with corrected value, | ||
- remove invalid items e.g. in arrays or maps, | ||
- react to changes in field names, e.g. due to schema migration (i.e. only one-directional changes that cannot be dealt with by alias operators). | ||
It is possible to define those functions by passing them as additional property arguments to the propSchema during its creation. | ||
```javascript | ||
const myHandler = { | ||
beforeDeserialize: function (callback, jsonValue, jsonParentValue, propNameOrIndex, context, propDef) { | ||
if (typeof jsonValue === 'string') { | ||
callback(null, jsonValue) | ||
} else if (typeof jsonValue === 'number') { | ||
callback(null, jsonValue.toString()) | ||
} else { | ||
callback(new Error('something went wrong before deserialization')) | ||
} | ||
}, | ||
afterDeserialize: function (callback, error, newValue, jsonValue, jsonParentValue, propNameOrIndex, context, | ||
propDef, numRetry) { | ||
if (!error && newValue !== 'needs change') { | ||
callback(null, newValue) | ||
} else if (!error && newValue === 'needs change') { | ||
callback(new Error(), 'changed value') | ||
} else { | ||
callback(error) | ||
} | ||
} | ||
} | ||
class MyData { | ||
@serializable(primitive(myHandler)) mySimpleField | ||
} | ||
``` | ||
A more detailed example can be found in [test/typescript/ts.ts](test/typescript/ts.ts). | ||
# API | ||
@@ -300,3 +341,3 @@ | ||
[src/serializr.js:52-52](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/serializr.js#L52-L52 "Source code on GitHub") | ||
[src/serializr.js:52-52](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/serializr.js#L52-L52 "Source code on GitHub") | ||
@@ -310,7 +351,7 @@ JSDOC type defintions for usage w/o typescript. | ||
- `value` **any** | ||
- `writeable` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `get` **([Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function) \| [undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined))** | ||
- `set` **([Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function) \| [undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined))** | ||
- `configurable` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `enumerable` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `writeable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `get` **([Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function) \| [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** | ||
- `set` **([Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function) \| [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** | ||
- `configurable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `enumerable` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `sourcePropertyValue` **any** | ||
@@ -326,3 +367,3 @@ - `jsonValue` **any** | ||
- `error` **any** | ||
- `id` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** | ||
- `id` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** | ||
- `callback` **cpsCallback** | ||
@@ -337,3 +378,3 @@ - `factory` | ||
- `deserializer` **deserializerFunction** | ||
- `identifier` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
- `identifier` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** | ||
@@ -350,3 +391,3 @@ Returns **any** any - serialized object | ||
[src/api/createSimpleSchema.js:17-24](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/api/createSimpleSchema.js#L17-L24 "Source code on GitHub") | ||
[src/api/createSimpleSchema.js:17-24](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/createSimpleSchema.js#L17-L24 "Source code on GitHub") | ||
@@ -376,3 +417,3 @@ Creates a model schema that (de)serializes from / to plain javascript objects. | ||
[src/api/createModelSchema.js:29-47](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/api/createModelSchema.js#L29-L47 "Source code on GitHub") | ||
[src/api/createModelSchema.js:29-47](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/createModelSchema.js#L29-L47 "Source code on GitHub") | ||
@@ -385,5 +426,5 @@ Creates a model schema that (de)serializes an object created by a constructor function (class). | ||
- `clazz` **([constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
- `clazz` **([constructor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
- `props` **[object](#object)** property mapping | ||
- `factory` **[function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** optional custom factory. Receives context as first arg | ||
- `factory` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** optional custom factory. Receives context as first arg | ||
@@ -411,3 +452,3 @@ **Examples** | ||
[src/api/getDefaultModelSchema.js:9-18](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/api/getDefaultModelSchema.js#L9-L18 "Source code on GitHub") | ||
[src/api/getDefaultModelSchema.js:9-18](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/getDefaultModelSchema.js#L9-L18 "Source code on GitHub") | ||
@@ -424,3 +465,3 @@ Returns the standard model schema associated with a class / constructor function | ||
[src/api/setDefaultModelSchema.js:15-18](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/api/setDefaultModelSchema.js#L15-L18 "Source code on GitHub") | ||
[src/api/setDefaultModelSchema.js:15-18](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/setDefaultModelSchema.js#L15-L18 "Source code on GitHub") | ||
@@ -436,3 +477,3 @@ Sets the default model schema for class / constructor function. | ||
- `clazz` **([constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
- `clazz` **([constructor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) | class)** class or constructor function | ||
- `modelSchema` **[ModelSchema](#modelschema)** a model schema | ||
@@ -444,3 +485,3 @@ | ||
[src/api/serializable.js:93-103](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/api/serializable.js#L93-L103 "Source code on GitHub") | ||
[src/api/serializable.js:93-103](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/api/serializable.js#L93-L103 "Source code on GitHub") | ||
@@ -469,3 +510,3 @@ Decorator that defines a new property mapping on the default model schema for the class | ||
[src/core/serialize.js:16-34](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/core/serialize.js#L16-L34 "Source code on GitHub") | ||
[src/core/serialize.js:16-34](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/serialize.js#L16-L34 "Source code on GitHub") | ||
@@ -485,3 +526,3 @@ Serializes an object (graph) into json using the provided model schema. | ||
[src/core/serialize.js:89-100](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/core/serialize.js#L89-L100 "Source code on GitHub") | ||
[src/core/serialize.js:89-100](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/serialize.js#L89-L100 "Source code on GitHub") | ||
@@ -494,7 +535,18 @@ The `serializeAll` decorator can be used on a class to signal that all primitive properties should be serialized automatically. | ||
## cancelDeserialize | ||
[src/core/cancelDeserialize.js:12-18](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/cancelDeserialize.js#L12-L18 "Source code on GitHub") | ||
Cancels an asynchronous deserialization or update operation for the specified target object. | ||
**Parameters** | ||
- `instance` object that was previously returned from deserialize or update method | ||
## deserialize | ||
[src/core/deserialize.js:38-56](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/core/deserialize.js#L38-L56 "Source code on GitHub") | ||
[src/core/deserialize.js:45-63](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/deserialize.js#L45-L63 "Source code on GitHub") | ||
Deserializes a json structor into an object graph. | ||
Deserializes a json structure into an object graph. | ||
This process might be asynchronous (for example if there are references with an asynchronous | ||
@@ -506,13 +558,16 @@ lookup function). The function returns an object (or array of objects), but the returned object | ||
- `schema` **([object](#object) \| [array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array))** to use for deserialization | ||
- `json` **[json](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)** data to deserialize | ||
- `callback` **[function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** node style callback that is invoked once the deserializaiton has finished. | ||
First argument is the optional error, second argument is the deserialized object (same as the return value) | ||
- `customArgs` **any** custom arguments that are available as `context.args` during the deserialization process. This can be used as dependency injection mechanism to pass in, for example, stores. | ||
- `schema` **([object](#object) \| [array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array))** to use for deserialization | ||
- `json` **[json](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON)** data to deserialize | ||
- `callback` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** node style callback that is invoked once the deserialization has | ||
finished. First argument is the optional error, second argument is the deserialized object | ||
(same as the return value) | ||
- `customArgs` **any** custom arguments that are available as `context.args` during the | ||
deserialization process. This can be used as dependency injection mechanism to pass in, for | ||
example, stores. | ||
Returns **([object](#object) \| [array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array))** deserialized object, possibly incomplete. | ||
Returns **([object](#object) \| [array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array))** deserialized object, possibly incomplete. | ||
## update | ||
[src/core/update.js:21-42](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/core/update.js#L21-L42 "Source code on GitHub") | ||
[src/core/update.js:22-44](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/core/update.js#L22-L44 "Source code on GitHub") | ||
@@ -528,11 +583,17 @@ Similar to deserialize, but updates an existing object instance. | ||
- `json` **[object](#object)** the json to deserialize | ||
- `callback` **[function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** the callback to invoke once deserialization has completed. | ||
- `callback` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** the callback to invoke once deserialization has completed. | ||
- `customArgs` **any** custom arguments that are available as `context.args` during the deserialization process. This can be used as dependency injection mechanism to pass in, for example, stores. | ||
Returns **([object](#object) \| [array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array))** deserialized object, possibly incomplete. | ||
## primitive | ||
[src/types/primitive.js:17-29](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/primitive.js#L17-L29 "Source code on GitHub") | ||
[src/types/primitive.js:18-32](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/primitive.js#L18-L32 "Source code on GitHub") | ||
Indicates that this field contains a primitive value (or Date) which should be serialized literally to json. | ||
**Parameters** | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
**Examples** | ||
@@ -553,3 +614,3 @@ | ||
[src/types/identifier.js:42-56](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/identifier.js#L42-L56 "Source code on GitHub") | ||
[src/types/identifier.js:43-66](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/identifier.js#L43-L66 "Source code on GitHub") | ||
@@ -566,3 +627,4 @@ Similar to primitive, but this field will be marked as the identifier for the given Model type. | ||
- `registerFn` **RegisterFunction** optional function to register this object during creation. | ||
- `arg1` **(RegisterFunction | AdditionalPropArgs)** optional registerFn: function to register this object during creation. | ||
- `arg2` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
@@ -595,9 +657,15 @@ **Examples** | ||
[src/types/date.js:8-23](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/date.js#L8-L23 "Source code on GitHub") | ||
[src/types/date.js:9-26](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/date.js#L9-L26 "Source code on GitHub") | ||
Similar to primitive, serializes instances of Date objects | ||
**Parameters** | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
Returns **PropSchema** | ||
## alias | ||
[src/types/alias.js:20-31](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/alias.js#L20-L31 "Source code on GitHub") | ||
[src/types/alias.js:20-33](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/alias.js#L20-L33 "Source code on GitHub") | ||
@@ -609,3 +677,3 @@ Alias indicates that this model property should be named differently in the generated json. | ||
- `name` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** name of the json field to be used for this property | ||
- `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** name of the json field to be used for this property | ||
- `propSchema` **PropSchema** propSchema to (de)serialize the contents of this field | ||
@@ -628,3 +696,3 @@ | ||
[src/types/custom.js:59-72](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/custom.js#L59-L72 "Source code on GitHub") | ||
[src/types/custom.js:60-75](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/custom.js#L60-L75 "Source code on GitHub") | ||
@@ -650,4 +718,5 @@ 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. | ||
- `serializer` **[function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** function that takes a model value and turns it into a json value | ||
- `deserializer` **[function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** function that takes a json value and turns it into a model value. It also takes context argument, which can allow you to deserialize based on the context of other parameters. | ||
- `serializer` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** function that takes a model value and turns it into a json value | ||
- `deserializer` **[function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** function that takes a json value and turns it into a model value. It also takes context argument, which can allow you to deserialize based on the context of other parameters. | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
@@ -694,3 +763,3 @@ **Examples** | ||
[src/types/object.js:34-52](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/object.js#L34-L52 "Source code on GitHub") | ||
[src/types/object.js:35-55](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/object.js#L35-L55 "Source code on GitHub") | ||
@@ -705,2 +774,3 @@ `object` indicates that this property contains an object that needs to be (de)serialized | ||
- `modelSchema` **[ModelSchema](#modelschema)** to be used to (de)serialize the object | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
@@ -733,3 +803,3 @@ **Examples** | ||
[src/types/reference.js:65-98](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/reference.js#L65-L98 "Source code on GitHub") | ||
[src/types/reference.js:66-105](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/reference.js#L66-L105 "Source code on GitHub") | ||
@@ -756,3 +826,4 @@ `reference` can be used to (de)serialize references that point to other models. | ||
- `target` : ModelSchema or string | ||
- `lookupFn` **RefLookupFunction** function | ||
- `lookupFn` **(RefLookupFunction | AdditionalPropArgs)** optional function or additionalArgs object | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
@@ -799,3 +870,3 @@ **Examples** | ||
[src/types/list.js:33-54](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/list.js#L33-L54 "Source code on GitHub") | ||
[src/types/list.js:42-104](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/list.js#L42-L104 "Source code on GitHub") | ||
@@ -808,2 +879,3 @@ List indicates that this property contains a list of things. | ||
- `propSchema` **PropSchema** to be used to (de)serialize the contents of the array | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
@@ -839,3 +911,3 @@ **Examples** | ||
[src/types/map.js:13-62](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/map.js#L13-L62 "Source code on GitHub") | ||
[src/types/map.js:14-65](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/map.js#L14-L65 "Source code on GitHub") | ||
@@ -849,21 +921,29 @@ Similar to list, but map represents a string keyed dynamic collection. | ||
- `propSchema` **any** | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
Returns **PropSchema** | ||
## mapAsArray | ||
[src/types/mapAsArray.js:15-52](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/mapAsArray.js#L15-L52 "Source code on GitHub") | ||
[src/types/mapAsArray.js:19-66](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/mapAsArray.js#L19-L66 "Source code on GitHub") | ||
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. | ||
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. | ||
**Parameters** | ||
- `propSchema` **any** , {string} keyPropertyName - the property of stored objects used as key in the map | ||
- `keyPropertyName` | ||
- `propSchema` **any** | ||
- `keyPropertyName` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** the property of stored objects used as key in the map | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
Returns **PropSchema** | ||
## raw | ||
[src/types/raw.js:15-24](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/types/raw.js#L15-L24 "Source code on GitHub") | ||
[src/types/raw.js:18-29](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/types/raw.js#L18-L29 "Source code on GitHub") | ||
@@ -873,2 +953,6 @@ Indicates that this field is only need to putted in the serialized json or | ||
**Parameters** | ||
- `additionalArgs` **AdditionalPropArgs** optional object that contains beforeDeserialize and/or afterDeserialize handlers | ||
**Examples** | ||
@@ -889,3 +973,3 @@ | ||
[src/constants.js:20-20](https://github.com/mobxjs/serializr/blob/b34674d96cb101c30739a497287d3c24ede31010/src/constants.js#L20-L20 "Source code on GitHub") | ||
[src/constants.js:20-20](https://github.com/:mobxjs/serializr/blob/e46f7980d4a3db201ce7f75c9f3b235acdc7c61f/src/constants.js#L20-L20 "Source code on GitHub") | ||
@@ -892,0 +976,0 @@ 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. |
@@ -8,2 +8,4 @@ // TODO: put this in the source files, and extract it, to preserve comments | ||
args: any; | ||
await(modelschema: ClazzOrModelSchema<any>,id:string,callback?: (err: any, result: any) => void):any; | ||
rootContext:Context; | ||
} | ||
@@ -13,5 +15,13 @@ | ||
export interface AdditionalPropArgs { | ||
beforeDeserialize?: BeforeDeserializeFunc; | ||
afterDeserialize?: AfterDeserializeFunc; | ||
} | ||
export interface PropSchema { | ||
serializer(sourcePropertyValue: any): any; | ||
deserializer(jsonValue: any, callback: (err: any, targetPropertyValue: any) => void, context: Context, currentPropertyValue: any): void; | ||
beforeDeserialize: BeforeDeserializeFunc; | ||
afterDeserialize: AfterDeserializeFunc; | ||
} | ||
@@ -31,2 +41,6 @@ | ||
export type AfterDeserializeFunc = (callback: (err: any, value: any) => void, err: any, newValue: any, jsonValue: any, jsonParentValue: any, propNameOrIndex: string | number, context: Context, propDef: PropSchema, numRetry: number) => void; | ||
export type BeforeDeserializeFunc = (callback: (err: any, value: any) => void, jsonValue: any, jsonParentValue: any, propNameOrIndex: string | number, context: Context, propDef: PropSchema) => void; | ||
export function createSimpleSchema<T extends Object>(props: Props): ModelSchema<T>; | ||
@@ -46,2 +60,4 @@ | ||
export function cancelDeserialize<T>(instance: T): void; | ||
export function deserialize<T>(modelschema: ClazzOrModelSchema<T>, jsonArray: any[], callback?: (err: any, result: T[]) => void, customArgs?: any): T[]; | ||
@@ -53,29 +69,32 @@ export function deserialize<T>(modelschema: ClazzOrModelSchema<T>, json: any, callback?: (err: any, result: T) => void, customArgs?: any): T; | ||
export function primitive(): PropSchema; | ||
export function primitive(additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function identifier(registerFn?: (id: any, value: any, context: Context) => void): PropSchema; | ||
export function identifier(registerFn?: (id: any, value: any, context: Context) => void, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function identifier(additionalArgs: AdditionalPropArgs): PropSchema; | ||
export function date(): PropSchema; | ||
export function date(additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function alias(jsonName: string, propSchema?: PropSchema | boolean): PropSchema; | ||
export function child(modelschema: ClazzOrModelSchema<any>): PropSchema; | ||
export function object(modelschema: ClazzOrModelSchema<any>): PropSchema; | ||
export function child(modelschema: ClazzOrModelSchema<any>, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function object(modelschema: ClazzOrModelSchema<any>, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export type RefLookupFunction = (id: string, callback: (err: any, result: any) => void) => void; | ||
export type RefLookupFunction = (id: string, callback: (err: any, result: any) => void,context:Context) => void; | ||
export type RegisterFunction = (id: any, object: any, context: Context) => void; | ||
export function ref(modelschema: ClazzOrModelSchema<any>, lookupFn?: RefLookupFunction): PropSchema; | ||
export function ref(identifierAttr: string, lookupFn: RefLookupFunction): PropSchema; | ||
export function reference(modelschema: ClazzOrModelSchema<any>, lookupFn?: RefLookupFunction): PropSchema; | ||
export function reference(identifierAttr: string, lookupFn: RefLookupFunction): PropSchema; | ||
export function ref(modelschema: ClazzOrModelSchema<any>, lookupFn?: RefLookupFunction, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function ref(modelschema: ClazzOrModelSchema<any>, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function ref(identifierAttr: string, lookupFn: RefLookupFunction, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function reference(modelschema: ClazzOrModelSchema<any>, lookupFn?: RefLookupFunction, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function reference(modelschema: ClazzOrModelSchema<any>, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function reference(identifierAttr: string, lookupFn: RefLookupFunction, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function list(propSchema: PropSchema): PropSchema; | ||
export function list(propSchema: PropSchema, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function map(propSchema: PropSchema): PropSchema; | ||
export function map(propSchema: PropSchema, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function mapAsArray(propSchema: PropSchema, keyPropertyName: string): PropSchema; | ||
export function mapAsArray(propSchema: PropSchema, keyPropertyName: string, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function custom(serializer: (value: any) => any, deserializer: (jsonValue: any, context?: any, oldValue?: any) => any): PropSchema; | ||
export function custom(serializer: (value: any) => any, deserializer: (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => any): PropSchema; | ||
export function custom(serializer: (value: any) => any, deserializer: (jsonValue: any, context?: any, oldValue?: any) => any, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
export function custom(serializer: (value: any) => any, deserializer: (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => any, additionalArgs?: AdditionalPropArgs): PropSchema; | ||
@@ -82,0 +101,0 @@ export function serializeAll<T extends Function>(clazz: T): T |
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
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
247046
2860
1205