Comparing version 1.0.0 to 1.1.0
# 0.0.1 | ||
# 1.1 | ||
Nothing changed yet! | ||
_Which should have been called 1.0...:-)_ | ||
* the `lookupFunction` of `ref` is now optional, if it is not provided, serializr will try to resolve the reference within the current document. Types are respected while resolving | ||
* `ref` has been renamed to `reference` | ||
* `child` has been renamed to `object` | ||
* `false` is now also an acceptable value for propSchema's | ||
* the prop schema `"*": true` now has the special meaning that all enumerable, primitive fields will be serialized. Will throw on non-primitive fields | ||
* introduced `custom(serializer, deserializer)` | ||
* `identifier` now supports an optional callback that can be used to register new instances in some store | ||
* circular dependency on default schema's for classes are now a bit better handled (but remain a fundamental JS problem, especially for classes) | ||
# 1.0 | ||
Initial release |
{ | ||
"name": "serializr", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Serialize and deserialize complex object graphs to JSON", | ||
@@ -8,3 +8,3 @@ "main": "serializr.js", | ||
"scripts": { | ||
"test": "tsc -p test/typescript && babel test/babel/babel.js -o test/babel/babel-compiled.js && node test/index", | ||
"test": "npm run build-test && node test/index", | ||
"lint": "eslint serializr.js", | ||
@@ -14,3 +14,6 @@ "prepublish": "npm run small-build && npm run build-docs", | ||
"build-docs": "documentation readme serializr.js --github --section API", | ||
"coverage": "istanbul cover tape test/*.js" | ||
"build-test": "npm run build-test-babel && npm run build-test-ts", | ||
"build-test-ts": "tsc -p test/typescript", | ||
"build-test-babel": "babel test/babel/babel.js -o test/babel/babel-compiled.js", | ||
"coverage": "npm run build-test && istanbul cover tape test/*.js" | ||
}, | ||
@@ -41,2 +44,4 @@ "keywords": [ | ||
"babel-plugin-transform-decorators-legacy": "^1.3.4", | ||
"babel-preset-es2015": "^6.9.0", | ||
"babel-preset-stage-1": "^6.5.0", | ||
"coveralls": "^2.11.9", | ||
@@ -43,0 +48,0 @@ "documentation": "^4.0.0-beta9", |
169
README.md
@@ -20,6 +20,6 @@ # Serializr | ||
- Supports inheritance | ||
- Works on any ES3+ environment. | ||
- Works on any ES5 environment (if ES3 is needed file a feature request) | ||
- Convenience decorators for ESNext / Typescript | ||
- Ships with typescript / flow(?) typings | ||
- Works well with MobX out of the box (but not limited too, the serialization mechanism is generic and MobX is not a dependency) | ||
- Ships with typescript / flow typings | ||
- Generic solution that works well with for example MobX out of the box | ||
@@ -39,3 +39,3 @@ Non-features: | ||
import { | ||
createModelSchema, primitive, ref, list, child, identifier, serialize, deserialize | ||
createModelSchema, primitive, reference, list, object, identifier, serialize, deserialize | ||
} from "serializr"; | ||
@@ -62,4 +62,4 @@ | ||
message: primitive(), | ||
author: ref(User, findUserById), | ||
comments: list(child(Message)) | ||
author: reference(User, findUserById), | ||
comments: list(object(Message)) | ||
}) | ||
@@ -91,3 +91,3 @@ | ||
import { | ||
createModelSchema, primitive, ref, list, child, identifier, serialize, deserialize, | ||
createModelSchema, primitive, reference, list, object, identifier, serialize, deserialize, | ||
serializable | ||
@@ -108,6 +108,6 @@ } from "serializr"; | ||
@serializable(ref(User, findUserById)) | ||
@serializable(reference(User, findUserById)) | ||
author = null; | ||
@serializable(list(child(Message))) | ||
@serializable(list(object(Message))) | ||
comments = []; | ||
@@ -179,4 +179,6 @@ } | ||
- `map(propSchema)`: Serializes an Map or string key based collection | ||
- `child(modelSchema)`: Serializes an child model element | ||
- `ref(modelSchema, lookupFunction)`: Serializes a reference to another model element | ||
- `object(modelSchema)`: Serializes an child model element | ||
- `reference(modelSchema, lookupFunction?)`: Serializes a reference to another model element | ||
- `custom(serializeFunction, deserializeFunction)`: Create your own property serializer by providing two functions, one that converts modelValue to jsonValue, and one that does the inverse | ||
- There is a special prop schema: `"*": true` that serializes all enumerable, non mentioned values as primitive | ||
@@ -214,3 +216,3 @@ It is possible to define your own prop schemas. You can define your own propSchema by creating a function that returns an object with the following signature: | ||
[serializr.js:83-90](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L83-L90 "Source code on GitHub") | ||
[serializr.js:79-86](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L79-L86 "Source code on GitHub") | ||
@@ -240,3 +242,3 @@ Creates a model schema that (de)serializes from / to plain javascript objects. | ||
[serializr.js:116-127](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L116-L127 "Source code on GitHub") | ||
[serializr.js:112-130](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L112-L130 "Source code on GitHub") | ||
@@ -274,3 +276,3 @@ Creates a model schema that (de)serializes an object created by a constructor function (class). | ||
[serializr.js:155-165](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L155-L165 "Source code on GitHub") | ||
[serializr.js:158-168](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L158-L168 "Source code on GitHub") | ||
@@ -296,3 +298,3 @@ Decorator that defines a new property mapping on the default model schema for the class | ||
[serializr.js:186-195](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L186-L195 "Source code on GitHub") | ||
[serializr.js:192-201](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L192-L201 "Source code on GitHub") | ||
@@ -310,3 +312,3 @@ Returns the standard model schema associated with a class / constructor function | ||
[serializr.js:208-211](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L208-L211 "Source code on GitHub") | ||
[serializr.js:214-217](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L214-L217 "Source code on GitHub") | ||
@@ -329,3 +331,3 @@ Sets the default model schema for class / constructor function. | ||
[serializr.js:251-269](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L251-L269 "Source code on GitHub") | ||
[serializr.js:269-287](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L269-L287 "Source code on GitHub") | ||
@@ -345,3 +347,3 @@ Serializes an object (graph) into json using the provided model schema. | ||
[serializr.js:306-324](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L306-L324 "Source code on GitHub") | ||
[serializr.js:341-359](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L341-L359 "Source code on GitHub") | ||
@@ -363,3 +365,3 @@ Deserializes an json structor into an object graph. | ||
[serializr.js:401-420](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L401-L420 "Source code on GitHub") | ||
[serializr.js:525-544](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L525-L544 "Source code on GitHub") | ||
@@ -380,3 +382,3 @@ Similar to deserialize, but updates an existing object instance. | ||
[serializr.js:442-454](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L442-L454 "Source code on GitHub") | ||
[serializr.js:566-578](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L566-L578 "Source code on GitHub") | ||
@@ -400,3 +402,3 @@ Indicates that this field contains a primitive value (or Date) which should be serialized literally to json. | ||
[serializr.js:462-466](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L462-L466 "Source code on GitHub") | ||
[serializr.js:613-627](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L613-L627 "Source code on GitHub") | ||
@@ -406,5 +408,39 @@ Similar to primitive, but this field will be marked as the identifier for the given Model type. | ||
Identifier accepts an optional `registerFn` with the signature: | ||
`(id, target, context) => void` | ||
that can be used to register this object in some store. note that not all fields of this object might have been deserialized yet | ||
**Parameters** | ||
- `registerFn` **[function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** optional function to register this object during creation. | ||
**Examples** | ||
```javascript | ||
var todos = {}; | ||
var s = _.createSimpleSchema({ | ||
id: _.identifier((id, object) => todos[id] = object), | ||
title: true | ||
}) | ||
_.deserialize(s, { | ||
id: 1, title: "test0" | ||
}) | ||
_.deserialize(s, [ | ||
{ id: 2, title: "test2" }, | ||
{ id: 1, title: "test1" } | ||
]) | ||
t.deepEqual(todos, { | ||
1: { id: 1, title: "test1" }, | ||
2: { id: 2, title: "test2" } | ||
}) | ||
``` | ||
Returns **PropSchema** | ||
## date | ||
[serializr.js:473-488](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L473-L488 "Source code on GitHub") | ||
[serializr.js:638-653](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L638-L653 "Source code on GitHub") | ||
@@ -415,3 +451,3 @@ Similar to primitive, serializes instances of Date objects | ||
[serializr.js:507-518](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L507-L518 "Source code on GitHub") | ||
[serializr.js:672-683](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L672-L683 "Source code on GitHub") | ||
@@ -440,7 +476,33 @@ Alias indicates that this model property should be named differently in the generated json. | ||
## child | ||
## custom | ||
[serializr.js:546-561](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L546-L561 "Source code on GitHub") | ||
[serializr.js:702-711](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L702-L711 "Source code on GitHub") | ||
Child indicates that this property contains an object that needs to be (de)serialized | ||
Can be used to create simple custom propSchema. | ||
**Parameters** | ||
- `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 | ||
**Examples** | ||
```javascript | ||
var schema = _.createSimpleSchema({ | ||
a: _.custom( | ||
function(v) { return v + 2 }, | ||
function(v) { return v - 2 } | ||
) | ||
}) | ||
t.deepEqual(_.serialize(s, { a: 4 }), { a: 6 }) | ||
t.deepEqual(_.deserialize(s, { a: 6 }), { a: 4 }) | ||
``` | ||
Returns **propSchema** | ||
## object | ||
[serializr.js:738-756](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L738-L756 "Source code on GitHub") | ||
`object` indicates that this property contains an object that needs to be (de)serialized | ||
using it's own model schema. | ||
@@ -462,3 +524,3 @@ | ||
title: true | ||
subTask: child(SubTask) | ||
subTask: object(SubTask) | ||
}) | ||
@@ -476,7 +538,7 @@ | ||
## ref | ||
## reference | ||
[serializr.js:613-636](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L613-L636 "Source code on GitHub") | ||
[serializr.js:810-843](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L810-L843 "Source code on GitHub") | ||
Ref can be used to (de)serialize references that points to other models. | ||
`reference` can be used to (de)serialize references that points to other models. | ||
@@ -495,2 +557,4 @@ The first parameter should be either a ModelSchema that has an `identifier()` property (see identifier) | ||
The lookupFunction is optional. If it is not provided, it will try to find an object of the expected type and required identifier within the same JSON document | ||
N.B. mind issues with circular dependencies when importing model schema's from other files! The module resolve algorithm might expose classes before `createModelSchema` is executed for the target class. | ||
@@ -513,3 +577,3 @@ | ||
createModelSchema(Post, { | ||
author: ref(User, findUserById) | ||
author: reference(User, findUserById) | ||
message: primitive() | ||
@@ -542,3 +606,3 @@ }) | ||
[serializr.js:662-683](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L662-L683 "Source code on GitHub") | ||
[serializr.js:875-896](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L875-L896 "Source code on GitHub") | ||
@@ -575,3 +639,3 @@ List indicates that this property contains a list of things. | ||
[serializr.js:697-746](https://github.com/mobxjs/serializr/blob/43860a7738285370caf8f7a99b9ffe32afe538a0/serializr.js#L697-L746 "Source code on GitHub") | ||
[serializr.js:910-959](https://github.com/mobxjs/serializr/blob/91507cc47fff9874b2839aef1af2786aefdcc310/serializr.js#L910-L959 "Source code on GitHub") | ||
@@ -595,4 +659,4 @@ Similar to list, but map represents a string keyed dynamic collection. | ||
task: primitive(), | ||
owner: ref("_userId", UserStore.findUserById) // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias(list(child(todoSchema)), "children") // recurse schema | ||
owner: reference("_userId", UserStore.findUserById) // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias("children", list(object(todoSchema))) | ||
} | ||
@@ -620,4 +684,4 @@ } | ||
task: primitive(), | ||
owner: ref("_userId", UserStore.findUserById) // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias(list(child(todoSchema)), "children") // recurse schema | ||
owner: reference("_userId", UserStore.findUserById) // attribute of the owner attribute of a todo + lookup function | ||
subTasks: alias("children", list(object(todoSchema))) | ||
} | ||
@@ -662,6 +726,6 @@ } | ||
@serializable(ref("_userId", UserStore.findUserById)) | ||
@serializable(reference("_userId", UserStore.findUserById)) | ||
owner = null; | ||
@serializable(alias(list(child(todoSchema)), "children") | ||
@serializable(alias("children", list(object(todoSchema))) | ||
subTasks = []; | ||
@@ -756,12 +820,4 @@ } | ||
id: identifier(), | ||
// context.target is the current arrow being deserialized | ||
// context.parentContext.target is the owner of the arrow; the store | ||
from: ref( | ||
Box, | ||
(id, callback, context) => findBox(context.parentContext.target, id, callback) | ||
), | ||
to: ref( | ||
Box, | ||
(id, callback, context) => findBox(context.parentContext.target, id, callback) | ||
) | ||
from: reference(Box) | ||
to: reference(Box) | ||
}) | ||
@@ -771,16 +827,8 @@ | ||
const storeModel = createSimpleSchema({ | ||
boxes: list(child(Box)), | ||
arrows: list(child(arrowModel)), | ||
boxes: list(object(Box)), | ||
arrows: list(object(arrowModel)), | ||
// context.target is the current store being deserialized | ||
selection: ref(Box, (id, callback, context) => findBox(context.target, id, callback)) | ||
selection: ref(Box) | ||
}) | ||
function findBox(store, id, callback) { | ||
const boxes = store.boxes.filter(box => box.id === id); | ||
if (boxes.length !== 1) | ||
callback("Invalid box ref: " + id); | ||
else | ||
callback(null, boxes[0]); | ||
} | ||
// example data | ||
@@ -817,1 +865,2 @@ store.boxes.push( | ||
- [ ] Support ImmutableJS out of the box | ||
- [ ] Make `"*": true` respect extends clauses |
316
serializr.js
@@ -53,10 +53,6 @@ (function() { | ||
function extend(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i] | ||
if (source && typeof source === "object") for (var key in source) | ||
if (source.hasOwnProperty(key)) | ||
target[key] = source[key] | ||
} | ||
return target | ||
function isPrimitive(value) { | ||
if (value === null) | ||
return true | ||
return typeof value !== "object" && typeof value !== "function" | ||
} | ||
@@ -121,2 +117,3 @@ | ||
var model = { | ||
targetClass: clazz, | ||
factory: factory || function() { | ||
@@ -127,2 +124,8 @@ return new clazz() | ||
} | ||
// find super model | ||
if (clazz.prototype.constructor !== Object) { | ||
var s = getDefaultModelSchema(clazz.prototype.constructor) | ||
if (s && s.targetClass !== clazz) | ||
model.extends = s | ||
} | ||
setDefaultModelSchema(clazz, model) | ||
@@ -174,4 +177,7 @@ return model | ||
var info = getDefaultModelSchema(target) | ||
if (!info) | ||
if (!info || !target.constructor.hasOwnProperty("serializeInfo")) | ||
info = createModelSchema(target.constructor, {}) | ||
if (info && info.targetClass !== target.constructor) | ||
// fixes typescript issue that tends to copy fields from super constructor to sub constructor in extends | ||
info = createModelSchema(target.constructor, {}) | ||
info.props[propName] = propSchema | ||
@@ -226,7 +232,7 @@ // MWE: why won't babel work without? | ||
function isAliasedPropSchema(propSchema) { | ||
return !!propSchema.jsonname | ||
return typeof propSchema === "object" && !!propSchema.jsonname | ||
} | ||
function isIdentifierPropSchema(propSchema) { | ||
return propSchema.identifier === true | ||
return typeof propSchema === "object" && propSchema.identifier === true | ||
} | ||
@@ -237,8 +243,20 @@ | ||
// optimizatoin: cache this lookup | ||
for (var propName in modelSchema.props) | ||
if (modelSchema.props[propName].identifier === true) | ||
return propName | ||
while (modelSchema) { | ||
for (var propName in modelSchema.props) | ||
if (typeof modelSchema.props[propName] === "object" && modelSchema.props[propName].identifier === true) | ||
return propName | ||
modelSchema = modelSchema.extends | ||
} | ||
return null | ||
} | ||
function isAssignableTo(actualType, expectedType) { | ||
while (actualType) { | ||
if (actualType === expectedType) | ||
return true | ||
actualType = actualType.extends | ||
} | ||
return false | ||
} | ||
/* | ||
@@ -283,8 +301,17 @@ * ## Serialization and deserialization | ||
res = serializeWithSchema(schema.extends, obj) | ||
else | ||
else { | ||
// TODO, make invariant?: invariant(!obj.constructor.prototype.constructor.serializeInfo, "object has a serializable supertype, but modelschema did not provide extends clause") | ||
res = {} | ||
} | ||
Object.keys(schema.props).forEach(function (key) { | ||
var propDef = schema.props[key] | ||
if (key === "*") { | ||
invariant(propDef === true, "prop schema '*' can onle be used with 'true'") | ||
serializeStarProps(schema, obj, res) | ||
return | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp | ||
if (propDef === false) | ||
return | ||
var jsonValue = propDef.serializer(obj[key]) | ||
@@ -296,2 +323,10 @@ res[propDef.jsonname || key] = jsonValue | ||
function serializeStarProps(schema, obj, target) { | ||
for (var key in obj) if (obj.hasOwnProperty(key)) if (!(key in schema.props)) { | ||
var value = obj[key] | ||
invariant(isPrimitive(value), "encountered non primitive value while serializing '*' properties in property '" + key + "': " + value) | ||
target[key] = value | ||
} | ||
} | ||
/* | ||
@@ -337,6 +372,7 @@ * Deserialization | ||
return void callback(null, null) | ||
var context = new Context(parentContext, json, callback, customArgs) | ||
var context = new Context(parentContext, schema, json, callback, customArgs) | ||
var target = schema.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 | ||
@@ -354,4 +390,11 @@ var lock = context.createCallback(GUARDED_NOOP) | ||
var propDef = schema.props[propName] | ||
if (propName === "*") { | ||
invariant(propDef === true, "prop schema '*' can onle be used with 'true'") | ||
deserializeStarProps(schema, target, json) | ||
return | ||
} | ||
if (propDef === true) | ||
propDef = _defaultPrimitiveProp | ||
if (propDef === false) | ||
return | ||
var jsonAttr = propDef.jsonname || propName | ||
@@ -362,3 +405,6 @@ if (!(jsonAttr in json)) | ||
json[jsonAttr], | ||
context.createCallback(function (value) { | ||
// 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) { | ||
target[propName] = value | ||
@@ -372,10 +418,36 @@ }), | ||
function Context(parentContext, json, onReadyCb, customArgs) { | ||
function schemaHasAlias(schema, name) { | ||
for (var key in schema.props) | ||
if (typeof schema.props[key] === "object" && schema.props[key].jsonname === name) | ||
return true | ||
return false | ||
} | ||
function deserializeStarProps(schema, obj, json) { | ||
for (var key in json) if (!(key in schema.props) && !schemaHasAlias(schema, key)) { | ||
var value = json[key] | ||
invariant(isPrimitive(value), "encountered non primitive value while deserializing '*' properties in property '" + key + "': " + value) | ||
obj[key] = value | ||
} | ||
} | ||
function Context(parentContext, modelSchema, json, onReadyCb, customArgs) { | ||
this.parentContext = parentContext | ||
this.isRoot = !parentContext | ||
this.pendingCallbacks = 0 | ||
this.pendingRefsCount = 0 | ||
this.onReadyCb = onReadyCb || GUARDED_NOOP | ||
this.json = json | ||
this.target = null | ||
this.pendingCallbacks = 0 | ||
this.hasError = false | ||
this.args = parentContext ? parentContext.args : customArgs | ||
this.modelSchema = modelSchema | ||
if (this.isRoot) { | ||
this.rootContext = this | ||
this.args = customArgs | ||
this.pendingRefs = {} // uuid: [{ modelSchema, uuid, cb }] | ||
this.resolvedRefs = {} // uuid: [{ modelSchema, value }] | ||
} else { | ||
this.rootContext = parentContext | ||
this.args = parentContext.args | ||
} | ||
} | ||
@@ -393,4 +465,15 @@ Context.prototype.createCallback = function (fn) { | ||
fn(value) | ||
if (--this.pendingCallbacks === 0) | ||
this.onReadyCb(null, this.target) | ||
if (--this.pendingCallbacks === this.pendingRefsCount) { | ||
if (this.pendingRefsCount > 0) | ||
// all pending callbacks are pending reference resolvers. not good. | ||
this.onReadyCb(new Error( | ||
"Unresolvable references in json: \"" + | ||
Object.keys(this.pendingRefs).filter(function (uuid) { | ||
return this.pendingRefs[uuid].length > 0 | ||
}, this).join("\", \"") + | ||
"\"" | ||
)) | ||
else | ||
this.onReadyCb(null, this.target) | ||
} | ||
} | ||
@@ -400,2 +483,43 @@ }.bind(this)) | ||
// given an object with uuid, modelSchema, callback, awaits until the given uuid is available | ||
// resolve immediately if possible | ||
Context.prototype.await = function (modelSchema, uuid, callback) { | ||
invariant(this.isRoot) | ||
if (uuid in this.resolvedRefs) { | ||
var match = this.resolvedRefs[uuid].filter(function (resolved) { | ||
return isAssignableTo(resolved.modelSchema, modelSchema) | ||
})[0] | ||
if (match) | ||
return void callback(null, match.value) | ||
} | ||
this.pendingRefsCount++ | ||
if (!this.pendingRefs[uuid]) | ||
this.pendingRefs[uuid] = [] | ||
this.pendingRefs[uuid].push({ | ||
modelSchema: modelSchema, | ||
uuid: uuid, | ||
callback: callback | ||
}) | ||
} | ||
// given a modelschema, uuid and value, resolve all references that where looking for this object | ||
Context.prototype.resolve = function(modelSchema, uuid, value) { | ||
invariant(this.isRoot) | ||
if (!this.resolvedRefs[uuid]) | ||
this.resolvedRefs[uuid] = [] | ||
this.resolvedRefs[uuid].push({ | ||
modelSchema: modelSchema, value: value | ||
}) | ||
if (uuid in this.pendingRefs) { | ||
for (var i = this.pendingRefs[uuid].length - 1; i >= 0; i--) { | ||
var opts = this.pendingRefs[uuid][i] | ||
if (isAssignableTo(modelSchema, opts.modelSchema)) { | ||
this.pendingRefs[uuid].splice(i, 1) | ||
this.pendingRefsCount-- | ||
opts.callback(null, value) | ||
} | ||
} | ||
} | ||
} | ||
/* | ||
@@ -429,3 +553,3 @@ * Update | ||
invariant(typeof target === "object" && target && !Array.isArray(target), "update needs an object") | ||
var context = new Context(null, json, callback, customArgs) | ||
var context = new Context(null, modelSchema, json, callback, customArgs) | ||
context.target = target | ||
@@ -460,7 +584,7 @@ var lock = context.createCallback(GUARDED_NOOP) | ||
serializer: function (value) { | ||
invariant(value === null || (typeof value !== "object" && typeof value !== "function"), "this value is not primitive: " + value) | ||
invariant(isPrimitive(value), "this value is not primitive: " + value) | ||
return value | ||
}, | ||
deserializer: function (jsonValue, done) { | ||
if ((typeof jsonValue === "object" && jsonValue !== null) || typeof jsonValue === "function") | ||
if (!isPrimitive(jsonValue)) | ||
return void done("[serializr] this value is not primitive: " + jsonValue) | ||
@@ -476,10 +600,51 @@ return void done(null, jsonValue) | ||
* | ||
* @returns | ||
* Identifier accepts an optional `registerFn` with the signature: | ||
* `(id, target, context) => void` | ||
* that can be used to register this object in some store. note that not all fields of this object might have been deserialized yet | ||
* | ||
* @example | ||
* var todos = {}; | ||
* | ||
* var s = _.createSimpleSchema({ | ||
* id: _.identifier((id, object) => todos[id] = object), | ||
* title: true | ||
* }) | ||
* | ||
* _.deserialize(s, { | ||
* id: 1, title: "test0" | ||
* }) | ||
* _.deserialize(s, [ | ||
* { id: 2, title: "test2" }, | ||
* { id: 1, title: "test1" } | ||
* ]) | ||
* | ||
* t.deepEqual(todos, { | ||
* 1: { id: 1, title: "test1" }, | ||
* 2: { id: 2, title: "test2" } | ||
* }) | ||
* | ||
* @param {function} registerFn optional function to register this object during creation. | ||
* | ||
* @returns {PropSchema} | ||
*/ | ||
function identifier() { | ||
return extend({ | ||
identifier: true | ||
}, _defaultPrimitiveProp) | ||
function identifier(registerFn) { | ||
invariant(!registerFn || typeof registerFn === "function", "First argument should be ommitted or function") | ||
return { | ||
identifier: true, | ||
serializer: _defaultPrimitiveProp.serializer, | ||
deserializer: function (jsonValue, done, context) { | ||
_defaultPrimitiveProp.deserializer(jsonValue, function(err, id) { | ||
defaultRegisterFunction(id, context.target, context) | ||
if (registerFn) | ||
registerFn(id, context.target, context) | ||
done(err, id) | ||
}) | ||
} | ||
} | ||
} | ||
function defaultRegisterFunction(id, value, context) { | ||
context.rootContext.resolve(context.modelSchema, id, context.target) | ||
} | ||
/** | ||
@@ -526,3 +691,3 @@ * Similar to primitive, serializes instances of Date objects | ||
invariant(name && typeof name === "string", "expected prop name as first argument") | ||
propSchema = propSchema || _defaultPrimitiveProp | ||
propSchema = (!propSchema || propSchema === true) ? _defaultPrimitiveProp : propSchema | ||
invariant(isPropSchema(propSchema), "expected prop schema as second argument") | ||
@@ -538,5 +703,32 @@ invariant(!isAliasedPropSchema(propSchema), "provided prop is already aliased") | ||
/** | ||
* Can be used to create simple custom propSchema. | ||
* | ||
* @example | ||
* var schema = _.createSimpleSchema({ | ||
* a: _.custom( | ||
* function(v) { return v + 2 }, | ||
* function(v) { return v - 2 } | ||
* ) | ||
* }) | ||
* t.deepEqual(_.serialize(s, { a: 4 }), { a: 6 }) | ||
* t.deepEqual(_.deserialize(s, { a: 6 }), { a: 4 }) | ||
* | ||
* @param {function} serializer function that takes a model value and turns it into a json value | ||
* @param {function} deserializer function that takes a json value and turns it into a model value | ||
* @returns {propSchema} | ||
*/ | ||
function custom(serializer, deserializer) { | ||
invariant(typeof serializer === "function", "first argument should be function") | ||
invariant(typeof deserializer === "function", "second argument should be function") | ||
return { | ||
serializer: serializer, | ||
deserializer: function (jsonValue, done) { | ||
done(null, deserializer(jsonValue)) | ||
} | ||
} | ||
} | ||
/** | ||
* Child indicates that this property contains an object that needs to be (de)serialized | ||
* `object` indicates that this property contains an object that needs to be (de)serialized | ||
* using it's own model schema. | ||
@@ -552,3 +744,3 @@ * | ||
* title: true | ||
* subTask: child(SubTask) | ||
* subTask: object(SubTask) | ||
* }) | ||
@@ -566,7 +758,8 @@ * | ||
*/ | ||
function child(modelSchema) { | ||
modelSchema = getDefaultModelSchema(modelSchema) | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema) | ||
function object(modelSchema) { | ||
invariant(typeof modelSchema === "object" || typeof modelSchema === "function", "No modelschema provided. If you are importing it from another file be aware of circular dependencies.") | ||
return { | ||
serializer: function (item) { | ||
modelSchema = getDefaultModelSchema(modelSchema) | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema) | ||
if (item === null || item === undefined) | ||
@@ -577,2 +770,4 @@ return item | ||
deserializer: function (childJson, done, context) { | ||
modelSchema = getDefaultModelSchema(modelSchema) | ||
invariant(isModelSchema(modelSchema), "expected modelSchema, got " + modelSchema) | ||
if (childJson === null || childJson === undefined) | ||
@@ -586,3 +781,3 @@ return void done(null, childJson) | ||
/** | ||
* Ref can be used to (de)serialize references that points to other models. | ||
* `reference` can be used to (de)serialize references that points to other models. | ||
* | ||
@@ -600,2 +795,4 @@ * The first parameter should be either a ModelSchema that has an `identifier()` property (see identifier) | ||
* | ||
* The lookupFunction is optional. If it is not provided, it will try to find an object of the expected type and required identifier within the same JSON document | ||
* | ||
* N.B. mind issues with circular dependencies when importing model schema's from other files! The module resolve algorithm might expose classes before `createModelSchema` is executed for the target class. | ||
@@ -610,3 +807,3 @@ * | ||
* createModelSchema(Post, { | ||
* author: ref(User, findUserById) | ||
* author: reference(User, findUserById) | ||
* message: primitive() | ||
@@ -638,19 +835,29 @@ * }) | ||
*/ | ||
function ref(target, lookupFn) { | ||
invariant(typeof lookupFn === "function", "second argument should be a lookup function") | ||
function reference(target, lookupFn) { | ||
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 target === "string") | ||
childIdentifierAttribute = target | ||
else { | ||
var modelSchema = getDefaultModelSchema(target) | ||
invariant(isModelSchema(modelSchema), "expected model schema or string as first argument for 'ref', got " + modelSchema) | ||
childIdentifierAttribute = getIdentifierProp(modelSchema) | ||
invariant(!!childIdentifierAttribute, "provided model schema doesn't define an identifier() property and cannot be used by 'ref'.") | ||
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") | ||
if (typeof target === "string") | ||
childIdentifierAttribute = target | ||
else { | ||
var modelSchema = getDefaultModelSchema(target) | ||
invariant(isModelSchema(modelSchema), "expected model schema or string as first argument for 'ref', got " + modelSchema) | ||
lookupFn = lookupFn || createDefaultRefLookup(modelSchema) | ||
childIdentifierAttribute = getIdentifierProp(modelSchema) | ||
invariant(!!childIdentifierAttribute, "provided model schema doesn't define an identifier() property and cannot be used by 'ref'.") | ||
} | ||
} | ||
return { | ||
serializer: function (item) { | ||
if (!initialized) | ||
initialize() | ||
return item ? item[childIdentifierAttribute] : null | ||
}, | ||
deserializer: function(identifierValue, done, context) { | ||
// TODO: refs should always be deserialized at the end of the root context! | ||
if (!initialized) | ||
initialize() | ||
if (identifierValue === null || identifierValue === undefined) | ||
@@ -664,2 +871,8 @@ done(null, identifierValue) | ||
function createDefaultRefLookup(modelSchema) { | ||
return function resolve(uuid, cb, context) { | ||
context.rootContext.await(modelSchema, uuid, cb) | ||
} | ||
} | ||
/** | ||
@@ -793,4 +1006,7 @@ * List indicates that this property contains a list of things. | ||
map: map, | ||
child: child, | ||
ref: ref | ||
object: object, | ||
child: object, // deprecate | ||
reference: reference, | ||
ref: reference, // deprecate | ||
custom: custom | ||
} | ||
@@ -797,0 +1013,0 @@ } |
/** serializr - (c) Michel Weststrate 2016 - MIT Licensed */ | ||
!function(){"use strict";function e(){function e(e){if(e)throw new Error(e)}function r(e){var r=!1;return function(){return r?void n(!1,"callback was invoked twice"):(r=!0,e.apply(null,arguments))}}function n(e,r){if(!e)throw new Error("[serializr] "+(r||"Illegal State"))}function t(e,r,n){if(0===e.length)return void n(null,[]);var t=e.length,i=[],o=!1,a=function(e,r,a){r?o||(o=!0,n(r)):(i[e]=a,0===--t&&n(null,i))};e.forEach(function(e,n){r(e,a.bind(null,n))})}function i(e){for(var r=1;r<arguments.length;r++){var n=arguments[r];if(n&&"object"==typeof n)for(var t in n)n.hasOwnProperty(t)&&(e[t]=n[t])}return e}function o(e){return{factory:function(){return{}},props:e}}function a(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={factory:t||function(){return new e},props:r};return c(e,i),i}function u(e,r,t){if(1===arguments.length){var i=e===!0?M:e;return n(d(i),"@serializable expects prop schema"),l.bind(null,i)}return l(k(),e,r,t)}function l(e,r,t,i){n(arguments.length>=2,"too few arguments. Please use @serializable as property decorator"),n("string"==typeof t,"incorrect usage of @serializable decorator");var o=s(r);return o||(o=a(r.constructor,{})),o.props[t]=e,!i||i.get||i.set||(i.writable=!0),i}function s(e){return e?f(e)?e:f(e.serializeInfo)?e.serializeInfo:e.constructor&&e.constructor.serializeInfo?e.constructor.serializeInfo:void 0:null}function c(e,r){return n(f(r)),e.serializeInfo=r}function f(e){return e&&e.factory&&e.props}function d(e){return e&&e.serializer&&e.deserializer}function p(e){return!!e.jsonname}function h(e){return e.identifier===!0}function v(e){n(f(e));for(var r in e.props)if(e.props[r].identifier===!0)return r;return null}function y(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=s(t[0]))}else i||(i=s(t));return n(!!i,"Failed to find default schema for "+e),Array.isArray(t)?t.map(function(e){return m(i,e)}):m(i,t)}function m(e,r){n(e&&"object"==typeof e,"Expected schema"),n(r&&"object"==typeof r,"Expected object");var t;return t=e.extends?m(e.extends,r):{},Object.keys(e.props).forEach(function(n){var i=e.props[n];i===!0&&(i=M);var o=i.serializer(r[n]);t[i.jsonname||n]=o}),t}function b(r,i,o,a){if(n(arguments.length>=2,"deserialize expects at least 2 arguments"),r=s(r),n(f(r),"first argument should be model schema"),Array.isArray(i)){var u=[];return t(i,function(e,n){var t=g(null,r,e,n,a);u.push(t)},o||e),u}return g(null,r,i,o,a)}function g(r,t,i,o,a){if(null===i||void 0===i)return void o(null,null);var u=new j(r,i,o,a),l=t.factory(u);n(!!l,"No object returned from factory"),u.target=l;var s=u.createCallback(e);return z(u,t,i,l),s(),l}function z(e,r,n,t){r.extends&&z(e,r.extends,n,t),Object.keys(r.props).forEach(function(i){var o=r.props[i];o===!0&&(o=M);var a=o.jsonname||i;a in n&&o.deserializer(n[a],e.createCallback(function(e){t[i]=e}),e,t[i])})}function j(r,n,t,i){this.parentContext=r,this.onReadyCb=t||e,this.json=n,this.target=null,this.pendingCallbacks=0,this.hasError=!1,this.args=r?r.args:i}function x(r,t,i,o,a){var u=2===arguments.length||"function"==typeof arguments[2];u&&(t=arguments[0],r=s(t),i=arguments[1],o=arguments[2],a=arguments[2]),n(f(r),"update failed to determine schema"),n("object"==typeof t&&t&&!Array.isArray(t),"update needs an object");var l=new j(null,i,o,a);l.target=t;var c=l.createCallback(e);z(l,r,i,t),c()}function k(){return{serializer:function(e){return n(null===e||"object"!=typeof e&&"function"!=typeof e,"this value is not primitive: "+e),e},deserializer:function(e,r){return"object"==typeof e&&null!==e||"function"==typeof e?void r("[serializr] this value is not primitive: "+e):void r(null,e)}}}function w(){return i({identifier:!0},M)}function E(){return{serializer:function(e){return null===e||void 0===e?e:(n(e instanceof Date,"Expected Date object"),e.getTime())},deserializer:function(e,r){return null===e||void 0===e?void r(null,e):void r(null,new Date(e))}}}function C(e,r){return n(e&&"string"==typeof e,"expected prop name as first argument"),r=r||M,n(d(r),"expected prop schema as second argument"),n(!p(r),"provided prop is already aliased"),{jsonname:e,serializer:r.serializer,deserializer:r.deserializer,identifier:h(r)}}function A(e){return e=s(e),n(f(e),"expected modelSchema, got "+e),{serializer:function(r){return null===r||void 0===r?r:y(e,r)},deserializer:function(r,n,t){return null===r||void 0===r?void n(null,r):void g(t,e,r,n)}}}function S(e,r){n("function"==typeof r,"second argument should be a lookup function");var t;if("string"==typeof e)t=e;else{var i=s(e);n(f(i),"expected model schema or string as first argument for 'ref', got "+i),t=v(i),n(!!t,"provided model schema doesn't define an identifier() property and cannot be used by 'ref'.")}return{serializer:function(e){return e?e[t]:null},deserializer:function(e,n,t){null===e||void 0===e?n(null,e):r(e,n,t)}}}function O(e){return e=e||M,n(d(e),"expected prop schema as second argument"),n(!p(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){return Array.isArray(r)?void t(r,function(r,n){return e.deserializer(r,n,i)},n):void n("[serializr] expected JSON array")}}}function I(e){return e&&"function"==typeof e.keys&&"function"==typeof e.clear}function D(e){return e=e||M,n(d(e),"expected prop schema as second argument"),n(!p(e),"provided prop is aliased, please put aliases first"),{serializer:function(r){n(r&&"object"==typeof r,"expected object or Map");var t=I(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)return void n("[serializr] expected JSON object");var o=Object.keys(r);O(e).deserializer(o.map(function(e){return r[e]}),function(e,r){if(e)return void n(e);var t,a=I(i);a?(i.clear(),t=i):t={};for(var u=0,l=o.length;u<l;u++)a?t.set(o[u],r[u]):t[o[u]]=r[u];n(null,t)},t)}}}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),0===--this.pendingCallbacks&&this.onReadyCb(null,this.target))}.bind(this))};var M=k();return{createModelSchema:a,createSimpleSchema:o,setDefaultModelSchema:s,getDefaultModelSchema:s,serializable:u,serialize:y,deserialize:b,update:x,primitive:k,identifier:w,date:E,alias:C,list:O,map:D,child:A,ref:S}}"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define("serializer",[],e):this.serializer=e()}(); | ||
!function(){"use strict";function e(){function e(e){if(e)throw new Error(e)}function r(e){var r=!1;return function(){return r?void t(!1,"callback was invoked twice"):(r=!0,e.apply(null,arguments))}}function t(e,r){if(!e)throw new Error("[serializr] "+(r||"Illegal State"))}function n(e,r,t){if(0===e.length)return void t(null,[]);var n=e.length,i=[],o=!1,s=function(e,r,s){r?o||(o=!0,t(r)):(i[e]=s,0===--n&&t(null,i))};e.forEach(function(e,t){r(e,s.bind(null,t))})}function i(e){return null===e||"object"!=typeof e&&"function"!=typeof e}function o(e){return{factory:function(){return{}},props:e}}function s(e,r,n){t(e!==Object,"one cannot simply put define a model schema for Object"),t("function"==typeof e,"expected constructor function");var i={targetClass:e,factory:n||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 l(e,i),i}function a(e,r,n){if(1===arguments.length){var i=e===!0?T:e;return t(p(i),"@serializable expects prop schema"),u.bind(null,i)}return u(S(),e,r,n)}function u(e,r,n,i){t(arguments.length>=2,"too few arguments. Please use @serializable as property decorator"),t("string"==typeof n,"incorrect usage of @serializable decorator");var o=c(r);return o&&r.constructor.hasOwnProperty("serializeInfo")||(o=s(r.constructor,{})),o&&o.targetClass!==r.constructor&&(o=s(r.constructor,{})),o.props[n]=e,!i||i.get||i.set||(i.writable=!0),i}function c(e){return e?f(e)?e:f(e.serializeInfo)?e.serializeInfo:e.constructor&&e.constructor.serializeInfo?e.constructor.serializeInfo:void 0:null}function l(e,r){return t(f(r)),e.serializeInfo=r}function f(e){return e&&e.factory&&e.props}function p(e){return e&&e.serializer&&e.deserializer}function d(e){return"object"==typeof e&&!!e.jsonname}function h(e){return"object"==typeof e&&e.identifier===!0}function v(e){for(t(f(e));e;){for(var r in e.props)if("object"==typeof e.props[r]&&e.props[r].identifier===!0)return r;e=e.extends}return null}function m(e,r){for(;e;){if(e===r)return!0;e=e.extends}return!1}function g(e,r){t(1===arguments.length||2===arguments.length,"serialize expects one or 2 arguments");var n=1===arguments.length?e:r,i=1===arguments.length?null:e;if(Array.isArray(n)){if(0===n.length)return[];i||(i=c(n[0]))}else i||(i=c(n));return t(!!i,"Failed to find default schema for "+e),Array.isArray(n)?n.map(function(e){return y(i,e)}):y(i,n)}function y(e,r){t(e&&"object"==typeof e,"Expected schema"),t(r&&"object"==typeof r,"Expected object");var n;return n=e.extends?y(e.extends,r):{},Object.keys(e.props).forEach(function(i){var o=e.props[i];if("*"===i)return t(o===!0,"prop schema '*' can onle be used with 'true'"),void b(e,r,n);if(o===!0&&(o=T),o!==!1){var s=o.serializer(r[i]);n[o.jsonname||i]=s}}),n}function b(e,r,n){for(var o in r)if(r.hasOwnProperty(o)&&!(o in e.props)){var s=r[o];t(i(s),"encountered non primitive value while serializing '*' properties in property '"+o+"': "+s),n[o]=s}}function z(r,i,o,s){if(t(arguments.length>=2,"deserialize expects at least 2 arguments"),r=c(r),t(f(r),"first argument should be model schema"),Array.isArray(i)){var a=[];return n(i,function(e,t){var n=j(null,r,e,t,s);a.push(n)},o||e),a}return j(null,r,i,o,s)}function j(r,n,i,o,s){if(null===i||void 0===i)return void o(null,null);var a=new w(r,n,i,o,s),u=n.factory(a);t(!!u,"No object returned from factory"),a.target=u;var c=a.createCallback(e);return x(a,n,i,u),c(),u}function x(e,r,n,i){r.extends&&x(e,r.extends,n,i),Object.keys(r.props).forEach(function(o){var s=r.props[o];if("*"===o)return t(s===!0,"prop schema '*' can onle be used with 'true'"),void C(r,i,n);if(s===!0&&(s=T),s!==!1){var a=s.jsonname||o;a in n&&s.deserializer(n[a],e.rootContext.createCallback(function(e){i[o]=e}),e,i[o])}})}function R(e,r){for(var t in e.props)if("object"==typeof e.props[t]&&e.props[t].jsonname===r)return!0;return!1}function C(e,r,n){for(var o in n)if(!(o in e.props||R(e,o))){var s=n[o];t(i(s),"encountered non primitive value while deserializing '*' properties in property '"+o+"': "+s),r[o]=s}}function w(r,t,n,i,o){this.parentContext=r,this.isRoot=!r,this.pendingCallbacks=0,this.pendingRefsCount=0,this.onReadyCb=i||e,this.json=n,this.target=null,this.hasError=!1,this.modelSchema=t,this.isRoot?(this.rootContext=this,this.args=o,this.pendingRefs={},this.resolvedRefs={}):(this.rootContext=r,this.args=r.args)}function k(r,n,i,o,s){var a=2===arguments.length||"function"==typeof arguments[2];a&&(n=arguments[0],r=c(n),i=arguments[1],o=arguments[2],s=arguments[2]),t(f(r),"update failed to determine schema"),t("object"==typeof n&&n&&!Array.isArray(n),"update needs an object");var u=new w(null,r,i,o,s);u.target=n;var l=u.createCallback(e);x(u,r,i,n),l()}function S(){return{serializer:function(e){return t(i(e),"this value is not primitive: "+e),e},deserializer:function(e,r){return i(e)?void r(null,e):void r("[serializr] this value is not primitive: "+e)}}}function E(e){return t(!e||"function"==typeof e,"First argument should be ommitted or function"),{identifier:!0,serializer:T.serializer,deserializer:function(r,t,n){T.deserializer(r,function(r,i){O(i,n.target,n),e&&e(i,n.target,n),t(r,i)})}}}function O(e,r,t){t.rootContext.resolve(t.modelSchema,e,t.target)}function A(){return{serializer:function(e){return null===e||void 0===e?e:(t(e instanceof Date,"Expected Date object"),e.getTime())},deserializer:function(e,r){return null===e||void 0===e?void r(null,e):void r(null,new Date(e))}}}function I(e,r){return t(e&&"string"==typeof e,"expected prop name as first argument"),r=r&&r!==!0?r:T,t(p(r),"expected prop schema as second argument"),t(!d(r),"provided prop is already aliased"),{jsonname:e,serializer:r.serializer,deserializer:r.deserializer,identifier:h(r)}}function D(e,r){return t("function"==typeof e,"first argument should be function"),t("function"==typeof r,"second argument should be function"),{serializer:e,deserializer:function(e,t){t(null,r(e))}}}function N(e){return t("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 e=c(e),t(f(e),"expected modelSchema, got "+e),null===r||void 0===r?r:g(e,r)},deserializer:function(r,n,i){return e=c(e),t(f(e),"expected modelSchema, got "+e),null===r||void 0===r?void n(null,r):void j(i,e,r,n)}}}function M(e,r){function n(){if(o=!0,t("string"!=typeof e||r,"if the reference target is specified by attribute name, a lookup function is required"),t(!r||"function"==typeof r,"second argument should be a lookup function"),"string"==typeof e)i=e;else{var n=c(e);t(f(n),"expected model schema or string as first argument for 'ref', got "+n),r=r||P(n),i=v(n),t(!!i,"provided model schema doesn't define an identifier() property and cannot be used by 'ref'.")}}t(!!e,"No modelschema provided. If you are importing it from another file be aware of circular dependencies.");var i,o=!1;return{serializer:function(e){return o||n(),e?e[i]:null},deserializer:function(e,t,i){o||n(),null===e||void 0===e?t(null,e):r(e,t,i)}}}function P(e){return function(r,t,n){n.rootContext.await(e,r,t)}}function F(e){return e=e||T,t(p(e),"expected prop schema as second argument"),t(!d(e),"provided prop is aliased, please put aliases first"),{serializer:function(r){return t(r&&"length"in r&&"map"in r,"expected array (like) object"),r.map(e.serializer)},deserializer:function(r,t,i){return Array.isArray(r)?void n(r,function(r,t){return e.deserializer(r,t,i)},t):void t("[serializr] expected JSON array")}}}function J(e){return e&&"function"==typeof e.keys&&"function"==typeof e.clear}function q(e){return e=e||T,t(p(e),"expected prop schema as second argument"),t(!d(e),"provided prop is aliased, please put aliases first"),{serializer:function(r){t(r&&"object"==typeof r,"expected object or Map");var n=J(r),i={};if(n)r.forEach(function(r,t){i[t]=e.serializer(r)});else for(var o in r)i[o]=e.serializer(r[o]);return i},deserializer:function(r,t,n,i){if(!r||"object"!=typeof r)return void t("[serializr] expected JSON object");var o=Object.keys(r);F(e).deserializer(o.map(function(e){return r[e]}),function(e,r){if(e)return void t(e);var n,s=J(i);s?(i.clear(),n=i):n={};for(var a=0,u=o.length;a<u;a++)s?n.set(o[a],r[a]):n[o[a]]=r[a];t(null,n)},n)}}}w.prototype.createCallback=function(e){return this.pendingCallbacks++,r(function(r,t){r?this.hasError||(this.hasError=!0,this.onReadyCb(r)):this.hasError||(e(t),--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))},w.prototype.await=function(e,r,n){if(t(this.isRoot),r in this.resolvedRefs){var i=this.resolvedRefs[r].filter(function(r){return m(r.modelSchema,e)})[0];if(i)return void n(null,i.value)}this.pendingRefsCount++,this.pendingRefs[r]||(this.pendingRefs[r]=[]),this.pendingRefs[r].push({modelSchema:e,uuid:r,callback:n})},w.prototype.resolve=function(e,r,n){if(t(this.isRoot),this.resolvedRefs[r]||(this.resolvedRefs[r]=[]),this.resolvedRefs[r].push({modelSchema:e,value:n}),r in this.pendingRefs)for(var i=this.pendingRefs[r].length-1;i>=0;i--){var o=this.pendingRefs[r][i];m(e,o.modelSchema)&&(this.pendingRefs[r].splice(i,1),this.pendingRefsCount--,o.callback(null,n))}};var T=S();return{createModelSchema:s,createSimpleSchema:o,setDefaultModelSchema:c,getDefaultModelSchema:c,serializable:a,serialize:g,deserialize:z,update:k,primitive:S,identifier:E,date:A,alias:I,list:F,map:q,object:N,child:N,reference:M,ref:M,custom:D}}"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define("serializer",[],e):this.serializer=e()}(); | ||
//# sourceMappingURL=serializr.min.js.map |
Sorry, the diff of this file is not supported yet
94966
981
836
12