Comparing version 1.0.3 to 2.0.0
{ | ||
"name": "typeson", | ||
"version": "1.0.3", | ||
"version": "2.0.0", | ||
"description": "JSON with types", | ||
@@ -5,0 +5,0 @@ "main": "typeson.js", |
133
test.js
var Typeson = require('./typeson'); | ||
var typeson = new Typeson(); | ||
var typeson = new Typeson().register({ | ||
Date: [ | ||
function (x) { return x instanceof Date; }, | ||
function (date) { return date.getTime(); }, | ||
function (time) { return new Date(time); } | ||
], | ||
Error: [ | ||
function (x) { return x instanceof Error; }, | ||
function (error) { return {name: error.name, message: error.message}; }, | ||
function (data) { | ||
var e = new Error (data.message); | ||
e.name = data.name; | ||
return e; | ||
} | ||
], | ||
SpecialNumber: [ | ||
function (x) { return typeof x === 'number' && isNaN(x) || x === Infinity || x === -Infinity; }, | ||
function (n) { return isNaN(n) ? "NaN" : n > 0 ? "Infinity" : "-Infinity" }, | ||
function (s) { return {NaN: NaN, Infinity: Infinity, "-Infinity": -Infinity}[s];} | ||
] | ||
}); | ||
var globalTypeson = typeson; | ||
// The test framework I need: | ||
@@ -94,2 +117,22 @@ function assert (x, msg) { | ||
}, function shouldSupportIntermedateTypes() { | ||
function CustomDate(date) { | ||
this._date = date; | ||
} | ||
var typeson = new Typeson() | ||
.register(globalTypeson.types) | ||
.register({ | ||
CustomDate: [ | ||
x => x instanceof CustomDate, | ||
cd => cd._date, | ||
date => new CustomDate(date) | ||
] | ||
}); | ||
var date = new Date(); | ||
var input = new CustomDate(new Date); | ||
var tson = typeson.stringify(input); | ||
console.log(tson); | ||
var back = typeson.parse(tson); | ||
assert (back instanceof CustomDate, "Should get CustomDate back"); | ||
assert (back._date.getTime() === date.getTime(), "Should have correct value"); | ||
}, function shouldRunReplacersRecursively(){ | ||
@@ -118,10 +161,11 @@ // | ||
var typeson = new Typeson(); | ||
typeson.register({ | ||
CustomDate: [ | ||
x => x instanceof CustomDate, | ||
cd => ({_date: cd.getRealDate(), name: cd.name}), | ||
obj => new CustomDate(obj._date, obj.name) | ||
] | ||
}); | ||
var typeson = new Typeson() | ||
.register(globalTypeson.types) | ||
.register({ | ||
CustomDate: [ | ||
x => x instanceof CustomDate, | ||
cd => ({_date: cd.getRealDate(), name: cd.name}), | ||
obj => new CustomDate(obj._date, obj.name) | ||
] | ||
}); | ||
var tson = typeson.stringify(input,null, 2); | ||
@@ -135,3 +179,72 @@ console.log(tson); | ||
}, function shouldBeAbleToStringifyComplexObjectsAtRoot() { | ||
var x = roundtrip(new Date(3)); | ||
assert (x instanceof Date, "x should be a Date"); | ||
assert (x.getTime() === 3, "Time should be 3"); | ||
var y = roundtrip([new Date(3)]); | ||
assert (y[0] instanceof Date, "y[0] should be a Date"); | ||
assert (y[0].getTime() === 3, "Time should be 3"); | ||
function Custom () { | ||
this.x = "oops"; | ||
} | ||
var TSON = new Typeson().register({ | ||
Custom: [ | ||
x => x instanceof Custom, | ||
s => false, | ||
f => new Custom() | ||
] | ||
}); | ||
var tson = TSON.stringify(new Custom()); | ||
console.log(tson); | ||
var z = TSON.parse(tson); | ||
assert (z instanceof Custom && z.x === "oops", "Custom type encapsulated in bool should work"); | ||
TSON = new Typeson().register({ | ||
Custom: [ | ||
x => x instanceof Custom, | ||
s => 42, | ||
f => new Custom() | ||
] | ||
}); | ||
tson = TSON.stringify(new Custom()); | ||
console.log(tson); | ||
z = TSON.parse(tson); | ||
assert (z instanceof Custom && z.x === "oops", "Custom type encapsulated in bool should work"); | ||
TSON = new Typeson().register({ | ||
Custom: [ | ||
x => x instanceof Custom, | ||
s => "foo", | ||
f => new Custom() | ||
] | ||
}); | ||
tson = TSON.stringify(new Custom()); | ||
console.log(tson); | ||
z = TSON.parse(tson); | ||
assert (z instanceof Custom && z.x === "oops", "Custom type encapsulated in bool should work"); | ||
}, function shouldBePossibleToEncapsulateObjectWithReserved$typesProperty() { | ||
function Custom (val, $types){ | ||
this.val = val; | ||
this.$types = $types; | ||
} | ||
var typeson = new Typeson().register({ | ||
Custom: [ | ||
x => x instanceof Custom, | ||
c => ({val: c.val, $types: c.$types}), | ||
o => new Custom(o.val, o.$types) | ||
] | ||
}); | ||
var input = new Custom("bar", "foo"); | ||
var tson = typeson.stringify(input); | ||
console.log(tson); | ||
var x = typeson.parse(tson); | ||
assert (x instanceof Custom, "Should get a Custom back"); | ||
assert (x.val === "bar", "Should have correct val value"); | ||
assert (x.$types === 'foo', "Should have correct $types value"); | ||
}, function shouldLeaveLeftOutType() { | ||
// Uint8Buffer is not registered. | ||
}]); | ||
414
typeson.js
@@ -1,248 +0,204 @@ | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
global.Typeson = factory(); | ||
}(this, function () { 'use strict'; | ||
var keys = Object.keys, | ||
isArray = Array.isArray; | ||
var keys = Object.keys, | ||
isArray = Array.isArray; | ||
/* Typeson - JSON with types | ||
* License: The MIT License (MIT) | ||
* Copyright (c) 2016 David Fahlander | ||
/* Typeson - JSON with types | ||
* License: The MIT License (MIT) | ||
* Copyright (c) 2016 David Fahlander | ||
*/ | ||
/** An instance of this class can be used to call stringify() and parse(). | ||
* Supports built-in types such as Date, Error, Regexp etc by default but can | ||
* also be extended to support custom types using the register() method. | ||
* Typeson also resolves cyclic references. | ||
* | ||
* @constructor | ||
* @param {{cyclic: boolean}} [options] - if cyclic (default true), cyclic references will be handled gracefully. | ||
*/ | ||
function Typeson (options) { | ||
// Replacers signature: replace (value). Returns falsy if not replacing. Otherwise ["Date", value.getTime()] | ||
var replacers = []; | ||
// Revivers: map {type => reviver}. Sample: {"Date": value => new Date(value)} | ||
var revivers = {}; | ||
/** Types registered via register() */ | ||
var regTypes = this.types = {}; | ||
/** Seraialize given object to Typeson. | ||
* | ||
* Arguments works identical to those of JSON.stringify(). | ||
*/ | ||
this.stringify = function (obj, replacer, space) { // replacer here has nothing to do with our replacers. | ||
return JSON.stringify (encapsulate(obj), replacer, space); | ||
} | ||
/** An instance of this class can be used to call stringify() and parse(). | ||
* Supports built-in types such as Date, Error, Regexp etc by default but can | ||
* also be extended to support custom types using the register() method. | ||
* Typeson also resolves cyclic references. | ||
/** Parse Typeson back into an obejct. | ||
* | ||
* @constructor | ||
* @param {{cyclic: boolean, types: Object}} [options] - if cyclic (default true), cyclic references will be handled gracefully. | ||
* If types is specified, the default built-in types will not be registered but instead the given types spec will be used. | ||
* Arguments works identical to those of JSON.parse(). | ||
*/ | ||
function Typeson (options) { | ||
options = options || {}; | ||
// Replacers signature: replace (value). Returns falsy if not replacing. Otherwise ["Date", value.getTime()] | ||
var replacers = []; | ||
// Revivers: map {type => reviver}. Sample: {"Date": value => new Date(value)} | ||
var revivers = {}; | ||
/** Types registered via register() */ | ||
var regTypes = this.types = {}; | ||
/** Seraialize given object to Typeson. | ||
* | ||
* Arguments works identical to those of JSON.stringify(). | ||
* | ||
* @param {Object} obj - Object to serialize. | ||
* @param {Function} [replacer] - Optional replacer function taking (key, value) and returning value. | ||
* @param {number} [space] - Optional space parameter to make the output prettier. | ||
*/ | ||
this.stringify = function (obj, replacer, space) { | ||
return JSON.stringify (encapsulate(obj), replacer, space); | ||
this.parse = function (text, reviver) { | ||
return revive (JSON.parse (text, reviver)); // This reviver has nothing to do with our revivers. | ||
} | ||
/** Encapsulate a complex object into a plain Object by replacing regisered types with | ||
* plain objects representing the types data. | ||
* | ||
* This method is used internally by Typeson.stringify(). | ||
* @param {Object} obj - Object to encapsulate. | ||
*/ | ||
var encapsulate = this.encapsulate = function (obj) { | ||
var types = {}, | ||
refObjs=[], // For checking cyclic references | ||
refKeys=[]; // For checking cyclic references | ||
// Clone the object deeply while at the same time replacing any special types or cyclic reference: | ||
var ret = _encapsulate ('', obj, options && ('cyclic' in options) ? options.cyclic : true); | ||
// Add $types to result only if we ever bumped into a special type | ||
if (keys(types).length) { | ||
// Special if array was serialized because JSON would ignore custom $types prop on an array. | ||
if (ret.constructor !== Object || ret.$types) return {$:ret, $types: {$: types}}; | ||
ret.$types = types; | ||
} | ||
return ret; | ||
/** Parse Typeson back into an obejct. | ||
* | ||
* Arguments works identical to those of JSON.parse(). | ||
* | ||
* @param {String} text - Typeson or JSON to parse. | ||
* @param {Function} [reviver] - Optional function taking (key, value) and returning value. | ||
*/ | ||
this.parse = function (text, reviver) { | ||
return revive (JSON.parse (text, reviver)); | ||
} | ||
/** Encapsulate a complex object into a plain Object that would survive JSON.stringify(). | ||
* This method is used internally by Typeson.stringify(). | ||
* @param {Object} obj - Object to encapsulate. | ||
*/ | ||
var encapsulate = this.encapsulate = function (obj) { | ||
var types = {}; | ||
// Clone the object deeply while at the same time replacing any special types or cyclic reference: | ||
var ret = traverse (obj, encapsulator, 'cyclic' in options ? options.cyclic : true, types); | ||
// Add $types to result only if we ever bumped into a special type | ||
if (keys(types).length) { | ||
// Special if array was serialized because JSON would ignore custom $types prop on an array. | ||
if (isArray(ret)) { | ||
var rv = {}; | ||
ret.forEach(function(v,i){rv[i] = v;}); | ||
types[""] = "[]"; | ||
ret = rv; | ||
function _encapsulate (keypath, value, cyclic) { | ||
var $typeof = typeof value; | ||
if ($typeof in {string:1, boolean:1, number:1, undefined:1 }) | ||
return $typeof === 'number' ? | ||
isNaN(value) || value === -Infinity || value === Infinity ? | ||
replace(keypath, value) : | ||
value : | ||
value; | ||
if (value == null) return value; | ||
if (cyclic) { | ||
// Options set to detect cyclic references and be able to rewrite them. | ||
var refIndex = refObjs.indexOf(value); | ||
if (refIndex < 0) { | ||
refObjs.push(value); | ||
refKeys.push(keypath); | ||
} else { | ||
types[keypath] = "#"; | ||
return '#'+refKeys[refIndex]; | ||
} | ||
ret.$types = types; | ||
} | ||
return ret; | ||
function encapsulator (key, value, clone, $typeof) { | ||
if ($typeof in {string:1, boolean:1, undefined:1}) return value; | ||
if ($typeof === 'number') { | ||
if (isNaN(value)) { | ||
return types[key] = "NaN"; | ||
var replaced = value.constructor === Object ? | ||
value : // Optimization: if plain object, don't try finding a replacer | ||
replace(keypath, value); | ||
if (replaced !== value) return replaced; | ||
if (value == null) return value; | ||
var clone; | ||
if (value.constructor === Object) | ||
clone = {}; | ||
else if (value.constructor === Array) | ||
clone = new Array(value.length); | ||
else return value; // Only clone vanilla objects and arrays. | ||
// Iterate object or array | ||
keys(value).forEach(function (key) { | ||
var val = _encapsulate(keypath + (keypath ? '.':'') + key, value[key], cyclic); | ||
if (val !== undefined) clone[key] = val; | ||
}); | ||
return clone; | ||
} | ||
function replace (key, value) { | ||
// Encapsulate registered types | ||
var i = replacers.length; | ||
while (i--) { | ||
if (replacers[i].test(value)) { | ||
var type = replacers[i].type; | ||
if (revivers[type]) { | ||
// Record the type only if a corresponding reviver exists. | ||
// This is to support specs where only replacement is done. | ||
// For example ensuring deep cloning of the object, or | ||
// replacing a type to its equivalent without the need to revive it. | ||
var existing = types[key]; | ||
// type can comprise an array of types (see test shouldSupportIntermediateTypes) | ||
types[key] = existing ? [type].concat(existing) : type; | ||
} | ||
if (value === Infinity) { | ||
return types[key] = "Infinity"; | ||
} | ||
if (value === -Infinity) { | ||
return types[key] = "-Infinity"; | ||
} | ||
return value; | ||
// Now, also traverse the result in case it contains it own types to replace | ||
return _encapsulate(key, replacers[i].replace(value), false); | ||
} | ||
// Optimization: Never try finding a replacer when value is a plain object. | ||
if (value.constructor === Object) return clone; | ||
// Encapsulate registered types | ||
var i = replacers.length; | ||
while (i--) { | ||
var replacement = replacers[i](value); | ||
if (replacement) { | ||
types[key] = replacement[0];// replacement[0] = Type Identifyer | ||
// Now, also traverse the result in case it contains it own types to replace | ||
return continueTraversing(replacement[1], key); | ||
} | ||
} | ||
return clone; | ||
} | ||
}; | ||
return value; | ||
} | ||
}; | ||
/** Revive an encapsulated object. | ||
* This method is used internally by JSON.parse(). | ||
* @param {Object} obj - Object corresponding to the Typeson spec. | ||
*/ | ||
var revive = this.revive = function (obj) { | ||
var types = obj.$types; | ||
if (!types) return obj; // No type info added. Revival not needed. | ||
return traverse (obj, function (key, value, clone, $typeof) { | ||
if (key === '$types') return; // return undefined to tell traverse to ignore it. | ||
var type = types[key]; | ||
if (!type) return clone; // This is the default (just a plain Object). | ||
if (type === '#') | ||
// Revive cyclic referenced object | ||
return getByKeyPath(target, value.substr(1)); // 1 === "#".length; | ||
var reviver = revivers[type]; | ||
if (!reviver) throw new Error ("Unregistered Type: " + type); | ||
return reviver(clone); | ||
}); | ||
//if (types[""] == "[]") return keys(rv).map(function (i){return rv[i]}); | ||
//return rv; | ||
}; | ||
/** Revive an encapsulated object. | ||
* This method is used internally by JSON.parse(). | ||
* @param {Object} obj - Object to revive. If it has $types member, the properties that are listed there | ||
* will be replaced with its true type instead of just plain objects. | ||
*/ | ||
var revive = this.revive = function (obj) { | ||
var types = obj.$types, | ||
ignore$Types = true; | ||
if (!types) return obj; // No type info added. Revival not needed. | ||
if (types.$ && types.$.constructor === Object) { | ||
// Special when root object is not a trivial Object, it will be encapsulated in $. | ||
obj = obj.$; | ||
types = types.$; | ||
ignore$Types = false; | ||
} | ||
return _revive ('', obj); | ||
/** Register custom types. | ||
* For examples how to use this method, search for "Register built-in types" in typeson.js. | ||
* @param {Array.<Object.<string,Function[]>>} typeSpec - Types and their functions [test, encapsulate, revive]; | ||
*/ | ||
var register = this.register = function (typeSpecSets) { | ||
[].concat(typeSpecSets).forEach(function (typeSpec) { | ||
keys(typeSpec).forEach(function (typeIdentifyer) { | ||
var spec = typeSpec[typeIdentifyer], | ||
test = spec[0], | ||
replace = spec[1], | ||
revive = spec[2], | ||
existingReviver = revivers[typeSpec]; | ||
if (existingReviver) { | ||
if (existingReviver.toString() !== revive.toString()) | ||
throw new Error ("Type " + typeIdentifyer + " is already registered with incompatible behaviour"); | ||
// Ignore re-registration if identical | ||
return; | ||
} | ||
function replacer (value) { | ||
return test(value) && [typeIdentifyer, replace(value)]; | ||
} | ||
replacers.push(replacer); | ||
revivers[typeIdentifyer] = revive; | ||
regTypes[typeIdentifyer] = spec; // Record to be retrueved via public types property. | ||
function _revive (keypath, value, target) { | ||
if (ignore$Types && keypath === '$types') return; | ||
var type = types[keypath]; | ||
if (value && (value.constructor === Object || value.constructor === Array)) { | ||
var clone = isArray(value) ? new Array(value.length) : {}; | ||
// Iterate object or array | ||
keys(value).forEach(function (key) { | ||
var val = _revive(keypath + (keypath ? '.':'') + key, value[key], target || clone); | ||
if (val !== undefined) clone[key] = val; | ||
}); | ||
value = clone; | ||
} | ||
if (!type) return value; | ||
if (type === '#') return getByKeyPath(target, value.substr(1)); | ||
return [].concat(type).reduce(function(val, type) { | ||
var reviver = revivers[type]; | ||
if (!reviver) throw new Error ("Unregistered type: " + type); | ||
return reviver(val); | ||
}, value); | ||
} | ||
}; | ||
/** Register types. | ||
* For examples how to use this method, see https://github.com/dfahlander/typeson-registry/tree/master/types | ||
* @param {Array.<Object.<string,Function[]>>} typeSpec - Types and their functions [test, encapsulate, revive]; | ||
*/ | ||
var register = this.register = function (typeSpecSets) { | ||
[].concat(typeSpecSets).forEach(function (typeSpec) { | ||
keys(typeSpec).forEach(function (typeIdentifyer) { | ||
var spec = typeSpec[typeIdentifyer], | ||
existingReplacer = replacers.filter(function(r){ return r.type === typeIdentifyer; }); | ||
if (existingReplacer.length) { | ||
// Remove existing spec and replace with this one. | ||
replacers.splice(replacers.indexOf(existingReplacer[0]), 1); | ||
delete revivers[typeIdentifyer]; | ||
delete regTypes[typeIdentifyer]; | ||
} | ||
if (spec) { | ||
replacers.push({ | ||
type: typeIdentifyer, | ||
test: spec[0], | ||
replace: spec[1] | ||
}); | ||
if (spec[2]) revivers[typeIdentifyer] = spec[2]; | ||
regTypes[typeIdentifyer] = spec; // Record to be retrieved via public types property. | ||
} | ||
}); | ||
return this; | ||
}; | ||
// | ||
// Setup Default Configuration | ||
// | ||
revivers.NaN = function() { return NaN; }; | ||
revivers.Infinity = function () { return Infinity; }; | ||
revivers["-Infinity"] = function () { return -Infinity; }; | ||
revivers["[]"] = function(a) { return keys(a).map(function (i){return a[i]}); }; // If root obj is an array (special) | ||
// Register option.types, or if not specified, the built-in types. | ||
this.register(options.types || { | ||
Date: [ | ||
function (x) { return x instanceof Date; }, | ||
function (date) { return date.getTime(); }, | ||
function (time) { return new Date(time); } | ||
], | ||
Error: [ | ||
function (x) { return x instanceof Error; }, | ||
function (error) { return {name: error.name, message: error.message}; }, | ||
function (data) { var e = new Error (data.message); e.name = data.name; return e; } | ||
] | ||
}); | ||
return this; | ||
}; | ||
} | ||
/** getByKeyPath() utility */ | ||
function getByKeyPath (obj, keyPath) { | ||
if (keyPath === '') return obj; | ||
var period = keyPath.indexOf('.'); | ||
if (period !== -1) { | ||
var innerObj = obj[keyPath.substr(0, period)]; | ||
return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1)); | ||
} | ||
return obj[keyPath]; | ||
} | ||
var refObjs, refKeys, target, replacer, types; | ||
function resetTraverse() { | ||
refObjs = []; | ||
refKeys = []; | ||
target = null; | ||
replacer = null; | ||
types = null; | ||
} | ||
/** traverse() utility */ | ||
function traverse (value, _replacer, cyclic, outTypes) { | ||
resetTraverse(); | ||
replacer = _replacer; | ||
types = outTypes || {}; | ||
var ret = continueTraversing(value, '', cyclic); | ||
resetTraverse(); // Free memory | ||
return ret; | ||
} | ||
function continueTraversing (value, keypath, cyclic) { | ||
var type = typeof value; | ||
// Don't add edge cases for NaN, Infinity or -Infinity here. Do such things in a replacer callback instead. | ||
if (type in {number:1, string:1, boolean:1, undefined:1, function:1, symbol:1}) | ||
return replacer (keypath, value, value, type); | ||
if (value === null) return null; | ||
if (cyclic) { | ||
// Options set to detect cyclic references and be able to rewrite them. | ||
var refIndex = refObjs.indexOf(value); | ||
if (refIndex < 0) { | ||
refObjs.push(value); | ||
refKeys.push(keypath); | ||
} else { | ||
types[keypath] = "#"; | ||
return '#'+refKeys[refIndex]; | ||
} | ||
} | ||
var clone = isArray(value) ? new Array(value.length) : {}; | ||
if (!target) target = clone; | ||
// Iterate object or array | ||
keys(value).forEach(function (key) { | ||
var val = continueTraversing(value[key], keypath + (keypath ? '.':'') + key, cyclic); | ||
if (val !== undefined) clone[key] = val; | ||
}); | ||
return replacer (keypath, value, clone, type); | ||
} | ||
/** getByKeyPath() utility */ | ||
function getByKeyPath (obj, keyPath) { | ||
if (keyPath === '') return obj; | ||
var period = keyPath.indexOf('.'); | ||
if (period !== -1) { | ||
var innerObj = obj[keyPath.substr(0, period)]; | ||
return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1)); | ||
} | ||
return obj[keyPath]; | ||
} | ||
// Mixin all instance properties to also be static properties so that | ||
// the Typeson constructor can be used as the default instance itself. | ||
var instance = new Typeson(); | ||
keys(instance).map(function(key){ Typeson[key] = instance[key];}); | ||
return Typeson; | ||
})); | ||
module.exports = Typeson; |
@@ -1,2 +0,2 @@ | ||
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):global.Typeson=factory()})(this,function(){"use strict";var keys=Object.keys,isArray=Array.isArray;function Typeson(options){options=options||{};var replacers=[];var revivers={};var regTypes=this.types={};this.stringify=function(obj,replacer,space){return JSON.stringify(encapsulate(obj),replacer,space)};this.parse=function(text,reviver){return revive(JSON.parse(text,reviver))};var encapsulate=this.encapsulate=function(obj){var types={};var ret=traverse(obj,encapsulator,"cyclic"in options?options.cyclic:true,types);if(keys(types).length){if(isArray(ret)){var rv={};ret.forEach(function(v,i){rv[i]=v});types[""]="[]";ret=rv}ret.$types=types}return ret;function encapsulator(key,value,clone,$typeof){if($typeof in{string:1,"boolean":1,undefined:1})return value;if($typeof==="number"){if(isNaN(value)){return types[key]="NaN"}if(value===Infinity){return types[key]="Infinity"}if(value===-Infinity){return types[key]="-Infinity"}return value}if(value.constructor===Object)return clone;var i=replacers.length;while(i--){var replacement=replacers[i](value);if(replacement){types[key]=replacement[0];return continueTraversing(replacement[1],key)}}return clone}};var revive=this.revive=function(obj){var types=obj.$types;if(!types)return obj;return traverse(obj,function(key,value,clone,$typeof){if(key==="$types")return;var type=types[key];if(!type)return clone;if(type==="#")return getByKeyPath(target,value.substr(1));var reviver=revivers[type];if(!reviver)throw new Error("Unregistered Type: "+type);return reviver(clone)})};var register=this.register=function(typeSpecSets){[].concat(typeSpecSets).forEach(function(typeSpec){keys(typeSpec).forEach(function(typeIdentifyer){var spec=typeSpec[typeIdentifyer],test=spec[0],replace=spec[1],revive=spec[2],existingReviver=revivers[typeSpec];if(existingReviver){if(existingReviver.toString()!==revive.toString())throw new Error("Type "+typeIdentifyer+" is already registered with incompatible behaviour");return}function replacer(value){return test(value)&&[typeIdentifyer,replace(value)]}replacers.push(replacer);revivers[typeIdentifyer]=revive;regTypes[typeIdentifyer]=spec})});return this};revivers.NaN=function(){return NaN};revivers.Infinity=function(){return Infinity};revivers["-Infinity"]=function(){return-Infinity};revivers["[]"]=function(a){return keys(a).map(function(i){return a[i]})};this.register(options.types||{Date:[function(x){return x instanceof Date},function(date){return date.getTime()},function(time){return new Date(time)}],Error:[function(x){return x instanceof Error},function(error){return{name:error.name,message:error.message}},function(data){var e=new Error(data.message);e.name=data.name;return e}]})}var refObjs,refKeys,target,replacer,types;function resetTraverse(){refObjs=[];refKeys=[];target=null;replacer=null;types=null}function traverse(value,_replacer,cyclic,outTypes){resetTraverse();replacer=_replacer;types=outTypes||{};var ret=continueTraversing(value,"",cyclic);resetTraverse();return ret}function continueTraversing(value,keypath,cyclic){var type=typeof value;if(type in{number:1,string:1,"boolean":1,undefined:1,"function":1,symbol:1})return replacer(keypath,value,value,type);if(value===null)return null;if(cyclic){var refIndex=refObjs.indexOf(value);if(refIndex<0){refObjs.push(value);refKeys.push(keypath)}else{types[keypath]="#";return"#"+refKeys[refIndex]}}var clone=isArray(value)?new Array(value.length):{};if(!target)target=clone;keys(value).forEach(function(key){var val=continueTraversing(value[key],keypath+(keypath?".":"")+key,cyclic);if(val!==undefined)clone[key]=val});return replacer(keypath,value,clone,type)}function getByKeyPath(obj,keyPath){if(keyPath==="")return obj;var period=keyPath.indexOf(".");if(period!==-1){var innerObj=obj[keyPath.substr(0,period)];return innerObj===undefined?undefined:getByKeyPath(innerObj,keyPath.substr(period+1))}return obj[keyPath]}var instance=new Typeson;keys(instance).map(function(key){Typeson[key]=instance[key]});return Typeson}); | ||
var keys=Object.keys,isArray=Array.isArray;function Typeson(options){var replacers=[];var revivers={};var regTypes=this.types={};this.stringify=function(obj,replacer,space){return JSON.stringify(encapsulate(obj),replacer,space)};this.parse=function(text,reviver){return revive(JSON.parse(text,reviver))};var encapsulate=this.encapsulate=function(obj){var types={},refObjs=[],refKeys=[];var ret=_encapsulate("",obj,options&&"cyclic"in options?options.cyclic:true);if(keys(types).length){if(ret.constructor!==Object||ret.$types)return{$:ret,$types:{$:types}};ret.$types=types}return ret;function _encapsulate(keypath,value,cyclic){var $typeof=typeof value;if($typeof in{string:1,"boolean":1,number:1,undefined:1})return $typeof==="number"?isNaN(value)||value===-Infinity||value===Infinity?replace(keypath,value):value:value;if(value==null)return value;if(cyclic){var refIndex=refObjs.indexOf(value);if(refIndex<0){refObjs.push(value);refKeys.push(keypath)}else{types[keypath]="#";return"#"+refKeys[refIndex]}}var replaced=value.constructor===Object?value:replace(keypath,value);if(replaced!==value)return replaced;if(value==null)return value;var clone;if(value.constructor===Object)clone={};else if(value.constructor===Array)clone=new Array(value.length);else return value;keys(value).forEach(function(key){var val=_encapsulate(keypath+(keypath?".":"")+key,value[key],cyclic);if(val!==undefined)clone[key]=val});return clone}function replace(key,value){var i=replacers.length;while(i--){if(replacers[i].test(value)){var type=replacers[i].type;if(revivers[type]){var existing=types[key];types[key]=existing?[type].concat(existing):type}return _encapsulate(key,replacers[i].replace(value),false)}}return value}};var revive=this.revive=function(obj){var types=obj.$types,ignore$Types=true;if(!types)return obj;if(types.$&&types.$.constructor===Object){obj=obj.$;types=types.$;ignore$Types=false}return _revive("",obj);function _revive(keypath,value,target){if(ignore$Types&&keypath==="$types")return;var type=types[keypath];if(value&&(value.constructor===Object||value.constructor===Array)){var clone=isArray(value)?new Array(value.length):{};keys(value).forEach(function(key){var val=_revive(keypath+(keypath?".":"")+key,value[key],target||clone);if(val!==undefined)clone[key]=val});value=clone}if(!type)return value;if(type==="#")return getByKeyPath(target,value.substr(1));return[].concat(type).reduce(function(val,type){var reviver=revivers[type];if(!reviver)throw new Error("Unregistered type: "+type);return reviver(val)},value)}};var register=this.register=function(typeSpecSets){[].concat(typeSpecSets).forEach(function(typeSpec){keys(typeSpec).forEach(function(typeIdentifyer){var spec=typeSpec[typeIdentifyer],existingReplacer=replacers.filter(function(r){return r.type===typeIdentifyer});if(existingReplacer.length){replacers.splice(replacers.indexOf(existingReplacer[0]),1);delete revivers[typeIdentifyer];delete regTypes[typeIdentifyer]}if(spec){replacers.push({type:typeIdentifyer,test:spec[0],replace:spec[1]});if(spec[2])revivers[typeIdentifyer]=spec[2];regTypes[typeIdentifyer]=spec}})});return this}}function getByKeyPath(obj,keyPath){if(keyPath==="")return obj;var period=keyPath.indexOf(".");if(period!==-1){var innerObj=obj[keyPath.substr(0,period)];return innerObj===undefined?undefined:getByKeyPath(innerObj,keyPath.substr(period+1))}return obj[keyPath]}module.exports=Typeson; | ||
//# sourceMappingURL=typeson.min.js.map |
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
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
429
27006
6
1
0