Comparing version 5.10.1 to 5.11.0
@@ -1,3 +0,26 @@ | ||
# typeson | ||
# typeson CHANGES | ||
## 5.11.0 | ||
- Fix: Ensure `testPlainObjects` are revived before reference | ||
resolution and setting (and before other types) so that | ||
objects-to-arrays will be properly referenced in all cases | ||
and can thus preserve non-index array properties (fixes #9) | ||
- Fix: Allow `iterateIn === 'object` to override array and create | ||
object from array | ||
- Fix (`reviveAsync`): Wait until all TypesonPromises resolve (no | ||
tests still) | ||
- Enhancement: Add state object option to auto-copy array length onto | ||
array-like object clone | ||
- Optimization: Avoid passing around (unchanging) `opts` within inner | ||
`revive` handling | ||
- Refactoring: Object property shorthand; avoid shadowing; make return | ||
explicit; use `slice` over `substr` | ||
- Docs: Further JSDoc | ||
- Testing: Report total passed | ||
- Testing (fix): Ensure using `@babel/polyfill` | ||
- npm/yarn: Update devDep | ||
- npm: Add yarn to `prepublishOnly` to ensure stays up to date with | ||
npm `package-lock.json` | ||
## 5.10.1 | ||
@@ -4,0 +27,0 @@ |
@@ -1,1 +0,1 @@ | ||
"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _slicedToArray(e,n){return _arrayWithHoles(e)||_iterableToArrayLimit(e,n)||_nonIterableRest()}function _toConsumableArray(e){return _arrayWithoutHoles(e)||_iterableToArray(e)||_nonIterableSpread()}function _arrayWithoutHoles(e){if(Array.isArray(e)){for(var n=0,t=new Array(e.length);n<e.length;n++)t[n]=e[n];return t}}function _arrayWithHoles(e){if(Array.isArray(e))return e}function _iterableToArray(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}function _iterableToArrayLimit(e,n){var t=[],r=!0,o=!1,i=void 0;try{for(var s,a=e[Symbol.iterator]();!(r=(s=a.next()).done)&&(t.push(s.value),!n||t.length!==n);r=!0);}catch(e){o=!0,i=e}finally{try{r||null==a.return||a.return()}finally{if(o)throw i}}return t}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance")}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}var keys=Object.keys,isArray=Array.isArray,_ref={},toString=_ref.toString,getProto=Object.getPrototypeOf,hasOwn={}.hasOwnProperty,fnToString=hasOwn.toString,internalStateObjPropsToIgnore=["type","replaced","iterateIn","iterateUnsetNumeric"];function isThenable(e,n){return Typeson.isObject(e)&&"function"==typeof e.then&&(!n||"function"==typeof e.catch)}function toStringTag(e){return toString.call(e).slice(8,-1)}function hasConstructorOf(e,n){if(!e||"object"!==_typeof(e))return!1;var t=getProto(e);if(!t)return!1;var r=hasOwn.call(t,"constructor")&&t.constructor;return"function"!=typeof r?null===n:"function"==typeof r&&null!==n&&fnToString.call(r)===fnToString.call(n)}function isPlainObject(e){return!(!e||"Object"!==toStringTag(e))&&(!getProto(e)||hasConstructorOf(e,Object))}function isUserObject(e){if(!e||"Object"!==toStringTag(e))return!1;var n=getProto(e);return!n||(hasConstructorOf(e,Object)||isUserObject(n))}function isObject(e){return e&&"object"===_typeof(e)}function Typeson(e){var n=[],t=[],r={},o=this.types={},i=this.stringify=function(n,t,r,o){o=Object.assign({},e,o,{stringification:!0});var i=a(n,null,o);return isArray(i)?JSON.stringify(i[0],t,r):i.then(function(e){return JSON.stringify(e,t,r)})};this.stringifySync=function(e,n,t,r){return i(e,n,t,Object.assign({},{throwOnBadSyncType:!0},r,{sync:!0}))},this.stringifyAsync=function(e,n,t,r){return i(e,n,t,Object.assign({},{throwOnBadSyncType:!0},r,{sync:!1}))};var s=this.parse=function(n,t,r){return r=Object.assign({},e,r,{parse:!0}),c(JSON.parse(n,t),r)};this.parseSync=function(e,n,t){return s(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!0}))},this.parseAsync=function(e,n,t){return s(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!1}))},this.specialTypeNames=function(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.returnTypeNames=!0,this.encapsulate(e,n,t)},this.rootTypeName=function(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.iterateNone=!0,this.encapsulate(e,n,t)};var a=this.encapsulate=function(o,i,s){var a=(s=Object.assign({sync:!0},e,s)).sync,c={},u=[],y=[],p=[],f=!(s&&"cyclic"in s)||s.cyclic,l=s.encapsulateObserver,h=_encapsulate("",o,f,i||{},p);function finish(e){var n=Object.values(c);if(s.iterateNone)return n.length?n[0]:Typeson.getJSONType(e);if(n.length){if(s.returnTypeNames)return _toConsumableArray(new Set(n));e&&isPlainObject(e)&&!e.hasOwnProperty("$types")?e.$types=c:e={$:e,$types:{$:c}}}else isObject(e)&&e.hasOwnProperty("$types")&&(e={$:e,$types:!0});return!s.returnTypeNames&&e}return p.length?a&&s.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():Promise.resolve(function checkPromises(e,n){return Promise.all(n.map(function(e){return e[1].p})).then(function(t){return Promise.all(t.map(function(t){var r=[],o=_slicedToArray(n.splice(0,1)[0],7),i=o[0],s=o[2],a=o[3],c=o[4],u=o[5],y=o[6],p=_encapsulate(i,t,s,a,r,!0,y),f=hasConstructorOf(p,TypesonPromise);return i&&f?p.p.then(function(n){return c[u]=n,checkPromises(e,r)}):(i?c[u]=p:e=f?p.p:p,checkPromises(e,r))}))}).then(function(){return e})}(h,p)).then(finish):!a&&s.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():s.stringification&&a?[finish(h)]:a?finish(h):Promise.resolve(finish(h));function _adaptBuiltinStateObjectProperties(e,n,t){Object.assign(e,n);var r=internalStateObjPropsToIgnore.map(function(n){var t=e[n];return delete e[n],t});t(),internalStateObjPropsToIgnore.forEach(function(n,t){e[n]=r[t]})}function _encapsulate(e,t,r,o,i,a,p){var f,h={},v=_typeof(t),b=l?function(n){var s=p||o.type||Typeson.getJSONType(t);l(Object.assign(n||h,{keypath:e,value:t,cyclic:r,stateObj:o,promisesData:i,resolvingTypesonPromise:a,awaitingTypesonPromise:hasConstructorOf(t,TypesonPromise)},void 0!==s?{type:s}:{}))}:null;if(v in{string:1,boolean:1,number:1,undefined:1})return void 0===t||"number"===v&&(isNaN(t)||t===-1/0||t===1/0)?(f=replace(e,t,o,i,!1,a,b))!==t&&(h={replaced:f}):f=t,b&&b(),f;if(null===t)return b&&b(),t;if(r&&!o.iterateIn&&!o.iterateUnsetNumeric){var d=u.indexOf(t);if(!(d<0))return c[e]="#",b&&b({cyclicKeypath:y[d]}),"#"+y[d];!0===r&&(u.push(t),y.push(e))}var O,T=isPlainObject(t),m=isArray(t),g=(T||m)&&(!n.length||o.replaced)||o.iterateIn?t:replace(e,t,o,i,T||m,null,b);if(g!==t?(f=g,h={replaced:g}):m||"array"===o.iterateIn?(O=new Array(t.length),h={clone:O}):T||"object"===o.iterateIn?h={clone:O={}}:""===e&&hasConstructorOf(t,TypesonPromise)?(i.push([e,t,r,o,void 0,void 0,o.type]),f=t):f=t,b&&b(),s.iterateNone)return O||f;if(!O)return f;if(o.iterateIn){var P=function _loop(n){var s={ownKeys:t.hasOwnProperty(n)};_adaptBuiltinStateObjectProperties(o,s,function(){var s=e+(e?".":"")+escapeKeyPathComponent(n),c=_encapsulate(s,t[n],!!r,o,i,a);hasConstructorOf(c,TypesonPromise)?i.push([s,c,!!r,o,O,n,o.type]):void 0!==c&&(O[n]=c)})};for(var S in t)P(S);b&&b({endIterateIn:!0,end:!0})}else keys(t).forEach(function(n){var s=e+(e?".":"")+escapeKeyPathComponent(n);_adaptBuiltinStateObjectProperties(o,{ownKeys:!0},function(){var e=_encapsulate(s,t[n],!!r,o,i,a);hasConstructorOf(e,TypesonPromise)?i.push([s,e,!!r,o,O,n,o.type]):void 0!==e&&(O[n]=e)})}),b&&b({endIterateOwn:!0,end:!0});if(o.iterateUnsetNumeric){for(var j=t.length,w=function _loop2(n){if(!(n in t)){var s=e+(e?".":"")+n;_adaptBuiltinStateObjectProperties(o,{ownKeys:!1},function(){var e=_encapsulate(s,void 0,!!r,o,i,a);hasConstructorOf(e,TypesonPromise)?i.push([s,e,!!r,o,O,n,o.type]):void 0!==e&&(O[n]=e)})}},A=0;A<j;A++)w(A);b&&b({endIterateUnsetNumeric:!0,end:!0})}return O}function replace(e,o,i,s,u,y,p){for(var l=u?n:t,h=l.length;h--;){var v=l[h];if(v.test(o,i)){var b=v.type;if(r[b]){var d=c[e];c[e]=d?[b].concat(d):b}return Object.assign(i,{type:b,replaced:!0}),!a&&v.replaceAsync||v.replace?(p&&p({replacing:!0}),_encapsulate(e,v[a||!v.replaceAsync?"replace":"replaceAsync"](o,i),f&&"readonly",i,s,y,b)):(p&&p({typeDetected:!0}),_encapsulate(e,o,f&&"readonly",i,s,y,b))}}return o}};this.encapsulateSync=function(e,n,t){return a(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!0}))},this.encapsulateAsync=function(e,n,t){return a(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!1}))};var c=this.revive=function(n,t){var o=(t=Object.assign({sync:!0},e,t)).sync,i=n&&n.$types,s=!0;if(!i)return n;if(!0===i)return n.$;i.$&&isPlainObject(i.$)&&(n=n.$,i=i.$,s=!1);var a=[],c={},u=function _revive(e,n,t,o,u,y){if(s&&"$types"===e)return;var p=i[e];if(isArray(n)||isPlainObject(n)){var f=isArray(n)?new Array(n.length):{};for(keys(n).forEach(function(r){var i=_revive(e+(e?".":"")+escapeKeyPathComponent(r),n[r],t||f,o,f,r);hasConstructorOf(i,Undefined)?f[r]=void 0:void 0!==i&&(f[r]=i)}),n=f;a.length;){var l=_slicedToArray(a[0],4),h=l[0],v=l[1],b=l[2],d=l[3],O=getByKeyPath(h,v);if(hasConstructorOf(O,Undefined))b[d]=void 0;else{if(void 0===O)break;b[d]=O}a.splice(0,1)}}if(!p)return n;if("#"===p){var T=getByKeyPath(t,n.substr(1));return void 0===T&&a.push([t,n.substr(1),u,y]),T}var m=o.sync;return[].concat(p).reduce(function(e,n){var t=r[n];if(!t)throw new Error("Unregistered type: "+n);return t[m&&t.revive?"revive":!m&&t.reviveAsync?"reviveAsync":"revive"](e,c)},n)}("",n,null,t);return isThenable(u=hasConstructorOf(u,Undefined)?void 0:u)?o&&t.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():u:!o&&t.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():o?u:Promise.resolve(u)};this.reviveSync=function(e,n){return c(e,Object.assign({},{throwOnBadSyncType:!0},n,{sync:!0}))},this.reviveAsync=function(e,n){return c(e,Object.assign({},{throwOnBadSyncType:!0},n,{sync:!1}))},this.register=function(e,i){return i=i||{},[].concat(e).forEach(function R(e){if(isArray(e))return e.map(R);e&&keys(e).forEach(function(s){if("#"===s)throw new TypeError("# cannot be used as a type name as it is reserved for cyclic objects");if(Typeson.JSON_TYPES.includes(s))throw new TypeError("Plain JSON object types are reserved as type names");var a=e[s],c=a.testPlainObjects?n:t,u=c.filter(function(e){return e.type===s});if(u.length&&(c.splice(c.indexOf(u[0]),1),delete r[s],delete o[s]),a){if("function"==typeof a){var y=a;a={test:function test(e){return e&&e.constructor===y},replace:function replace(e){return assign({},e)},revive:function revive(e){return assign(Object.create(y.prototype),e)}}}else isArray(a)&&(a={test:a[0],replace:a[1],revive:a[2]});var p={type:s,test:a.test.bind(a)};a.replace&&(p.replace=a.replace.bind(a)),a.replaceAsync&&(p.replaceAsync=a.replaceAsync.bind(a));var f="number"==typeof i.fallback?i.fallback:i.fallback?0:1/0;if(a.testPlainObjects?n.splice(f,0,p):t.splice(f,0,p),a.revive||a.reviveAsync){var l={};a.revive&&(l.revive=a.revive.bind(a)),a.reviveAsync&&(l.reviveAsync=a.reviveAsync.bind(a)),r[s]=l}o[s]=a}})}),this}}function assign(e,n){return keys(n).map(function(t){e[t]=n[t]}),e}function escapeKeyPathComponent(e){return e.replace(/~/g,"~0").replace(/\./g,"~1")}function unescapeKeyPathComponent(e){return e.replace(/~1/g,".").replace(/~0/g,"~")}function getByKeyPath(e,n){if(""===n)return e;var t=n.indexOf(".");if(t>-1){var r=e[unescapeKeyPathComponent(n.substr(0,t))];return void 0===r?void 0:getByKeyPath(r,n.substr(t+1))}return e[unescapeKeyPathComponent(n)]}function Undefined(){}function TypesonPromise(e){this.p=new Promise(e)}"undefined"!=typeof Symbol&&(TypesonPromise.prototype[Symbol.toStringTag]="TypesonPromise"),TypesonPromise.prototype.then=function(e,n){var t=this;return new TypesonPromise(function(r,o){t.p.then(function(n){r(e?e(n):n)},function(e){t.p.catch(function(e){return n?n(e):Promise.reject(e)}).then(r,o)})})},TypesonPromise.prototype.catch=function(e){return this.then(null,e)},TypesonPromise.resolve=function(e){return new TypesonPromise(function(n){n(e)})},TypesonPromise.reject=function(e){return new TypesonPromise(function(n,t){t(e)})},["all","race"].map(function(e){TypesonPromise[e]=function(n){return new TypesonPromise(function(t,r){Promise[e](n.map(function(e){return e.p})).then(t,r)})}}),Typeson.Undefined=Undefined,Typeson.Promise=TypesonPromise,Typeson.isThenable=isThenable,Typeson.toStringTag=toStringTag,Typeson.hasConstructorOf=hasConstructorOf,Typeson.isObject=isObject,Typeson.isPlainObject=isPlainObject,Typeson.isUserObject=isUserObject,Typeson.escapeKeyPathComponent=escapeKeyPathComponent,Typeson.unescapeKeyPathComponent=unescapeKeyPathComponent,Typeson.getByKeyPath=getByKeyPath,Typeson.getJSONType=function(e){return null===e?"null":isArray(e)?"array":_typeof(e)},Typeson.JSON_TYPES=["null","boolean","number","string","array","object"],module.exports=Typeson; | ||
"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function asyncGeneratorStep(e,t,n,r,o,i,a){try{var s=e[i](a),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,o)}function _asyncToGenerator(e){return function(){var t=this,n=arguments;return new Promise(function(r,o){var i=e.apply(t,n);function _next(e){asyncGeneratorStep(i,r,o,_next,_throw,"next",e)}function _throw(e){asyncGeneratorStep(i,r,o,_next,_throw,"throw",e)}_next(void 0)})}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function _createClass(e,t,n){return t&&_defineProperties(e.prototype,t),n&&_defineProperties(e,n),e}function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function _objectSpread(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){_defineProperty(e,t,n[t])})}return e}function _slicedToArray(e,t){return _arrayWithHoles(e)||_iterableToArrayLimit(e,t)||_nonIterableRest()}function _toConsumableArray(e){return _arrayWithoutHoles(e)||_iterableToArray(e)||_nonIterableSpread()}function _arrayWithoutHoles(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t<e.length;t++)n[t]=e[t];return n}}function _arrayWithHoles(e){if(Array.isArray(e))return e}function _iterableToArray(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}function _iterableToArrayLimit(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{r||null==s.return||s.return()}finally{if(o)throw i}}return n}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance")}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}var TypesonPromise=function TypesonPromise(e){_classCallCheck(this,TypesonPromise),this.p=new Promise(e)};"undefined"!=typeof Symbol&&(TypesonPromise.prototype[Symbol.toStringTag]="TypesonPromise"),TypesonPromise.prototype.then=function(e,t){var n=this;return new TypesonPromise(function(r,o){n.p.then(function(t){r(e?e(t):t)},function(e){n.p.catch(function(e){return t?t(e):Promise.reject(e)}).then(r,o)})})},TypesonPromise.prototype.catch=function(e){return this.then(null,e)},TypesonPromise.resolve=function(e){return new TypesonPromise(function(t){t(e)})},TypesonPromise.reject=function(e){return new TypesonPromise(function(t,n){n(e)})},["all","race"].map(function(e){TypesonPromise[e]=function(t){return new TypesonPromise(function(n,r){Promise[e](t.map(function(e){return e.p})).then(n,r)})}});var _ref={},toString=_ref.toString,hasOwn={}.hasOwnProperty,getProto=Object.getPrototypeOf,fnToString=hasOwn.toString;function isThenable(e,t){return isObject(e)&&"function"==typeof e.then&&(!t||"function"==typeof e.catch)}function toStringTag(e){return toString.call(e).slice(8,-1)}function hasConstructorOf(e,t){if(!e||"object"!==_typeof(e))return!1;var n=getProto(e);if(!n)return!1;var r=hasOwn.call(n,"constructor")&&n.constructor;return"function"!=typeof r?null===t:"function"==typeof r&&null!==t&&fnToString.call(r)===fnToString.call(t)}function isPlainObject(e){return!(!e||"Object"!==toStringTag(e))&&(!getProto(e)||hasConstructorOf(e,Object))}function isUserObject(e){if(!e||"Object"!==toStringTag(e))return!1;var t=getProto(e);return!t||(hasConstructorOf(e,Object)||isUserObject(t))}function isObject(e){return e&&"object"===_typeof(e)}function escapeKeyPathComponent(e){return e.replace(/~/g,"~0").replace(/\./g,"~1")}function unescapeKeyPathComponent(e){return e.replace(/~1/g,".").replace(/~0/g,"~")}function getByKeyPath(e,t){if(""===t)return e;var n=t.indexOf(".");if(n>-1){var r=e[unescapeKeyPathComponent(t.substr(0,n))];return void 0===r?void 0:getByKeyPath(r,t.substr(n+1))}return e[unescapeKeyPathComponent(t)]}function setAtKeyPath(e,t,n){if(""===t)return n;var r=t.indexOf(".");return r>-1?setAtKeyPath(e[unescapeKeyPathComponent(t.substr(0,r))],t.substr(r+1),n):(e[unescapeKeyPathComponent(t)]=n,e)}function getJSONType(e){return null===e?"null":Array.isArray(e)?"array":_typeof(e)}var keys=Object.keys,isArray=Array.isArray,hasOwn$1={}.hasOwnProperty,internalStateObjPropsToIgnore=["type","replaced","iterateIn","iterateUnsetNumeric"];function nestedPathsFirst(e,t){var n=e.keypath.match(/\./g),r=e.keypath.match(/\./g);return n&&(n=n.length),r&&(r=r.length),n>r?-1:r<n?1:e.keypath<t.keypath?-1:e.keypath>t.keypath}var Typeson=function(){function Typeson(e){_classCallCheck(this,Typeson),this.options=e,this.plainObjectReplacers=[],this.nonplainObjectReplacers=[],this.revivers={},this.types={}}return _createClass(Typeson,[{key:"stringify",value:function stringify(e,t,n,r){r=_objectSpread({},this.options,r,{stringification:!0});var o=this.encapsulate(e,null,r);return isArray(o)?JSON.stringify(o[0],t,n):o.then(function(e){return JSON.stringify(e,t,n)})}},{key:"stringifySync",value:function stringifySync(e,t,n,r){return this.stringify(e,t,n,_objectSpread({throwOnBadSyncType:!0},r,{sync:!0}))}},{key:"stringifyAsync",value:function stringifyAsync(e,t,n,r){return this.stringify(e,t,n,_objectSpread({throwOnBadSyncType:!0},r,{sync:!1}))}},{key:"parse",value:function parse(e,t,n){return n=_objectSpread({},this.options,n,{parse:!0}),this.revive(JSON.parse(e,t),n)}},{key:"parseSync",value:function parseSync(e,t,n){return this.parse(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!0}))}},{key:"parseAsync",value:function parseAsync(e,t,n){return this.parse(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!1}))}},{key:"specialTypeNames",value:function specialTypeNames(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return n.returnTypeNames=!0,this.encapsulate(e,t,n)}},{key:"rootTypeName",value:function rootTypeName(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return n.iterateNone=!0,this.encapsulate(e,t,n)}},{key:"encapsulate",value:function encapsulate(e,t,n){var r=(n=_objectSpread({sync:!0},this.options,n)).sync,o=this,i={},a=[],s=[],c=[],u=!(n&&"cyclic"in n)||n.cyclic,y=n.encapsulateObserver,p=_encapsulate("",e,u,t||{},c);function finish(e){var t=Object.values(i);if(n.iterateNone)return t.length?t[0]:Typeson.getJSONType(e);if(t.length){if(n.returnTypeNames)return _toConsumableArray(new Set(t));e&&isPlainObject(e)&&!hasOwn$1.call(e,"$types")?e.$types=i:e={$:e,$types:{$:i}}}else isObject(e)&&hasOwn$1.call(e,"$types")&&(e={$:e,$types:!0});return!n.returnTypeNames&&e}function checkPromises(e,t){return _checkPromises.apply(this,arguments)}function _checkPromises(){return(_checkPromises=_asyncToGenerator(regeneratorRuntime.mark(function _callee2(e,t){var n;return regeneratorRuntime.wrap(function _callee2$(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,Promise.all(t.map(function(e){return e[1].p}));case 2:return n=r.sent,r.next=5,Promise.all(n.map(function(){var n=_asyncToGenerator(regeneratorRuntime.mark(function _callee(n){var r,o,i,a,s,c,u,y,p,l,f,h,v,d;return regeneratorRuntime.wrap(function _callee$(b){for(;;)switch(b.prev=b.next){case 0:if(r=[],o=t.splice(0,1),i=_slicedToArray(o,1),a=i[0],s=_slicedToArray(a,7),c=s[0],u=s[2],y=s[3],p=s[4],l=s[5],f=s[6],h=_encapsulate(c,n,u,y,r,!0,f),v=hasConstructorOf(h,TypesonPromise),!c||!v){b.next=11;break}return b.next=8,h.p;case 8:return d=b.sent,p[l]=d,b.abrupt("return",checkPromises(e,r));case 11:return c?p[l]=h:e=v?h.p:h,b.abrupt("return",checkPromises(e,r));case 13:case"end":return b.stop()}},_callee,this)}));return function(e){return n.apply(this,arguments)}}()));case 5:return r.abrupt("return",e);case 6:case"end":return r.stop()}},_callee2,this)}))).apply(this,arguments)}function _adaptBuiltinStateObjectProperties(e,t,n){Object.assign(e,t);var r=internalStateObjPropsToIgnore.map(function(t){var n=e[t];return delete e[t],n});n(),internalStateObjPropsToIgnore.forEach(function(t,n){e[t]=r[n]})}function _encapsulate(e,t,r,c,u,p,l){var f,h={},v=_typeof(t),d=y?function(n){var o=l||c.type||Typeson.getJSONType(t);y(Object.assign(n||h,{keypath:e,value:t,cyclic:r,stateObj:c,promisesData:u,resolvingTypesonPromise:p,awaitingTypesonPromise:hasConstructorOf(t,TypesonPromise)},void 0!==o?{type:o}:{}))}:null;if(["string","boolean","number","undefined"].includes(v))return void 0===t||"number"===v&&(isNaN(t)||t===-1/0||t===1/0)?(f=replace(e,t,c,u,!1,p,d))!==t&&(h={replaced:f}):f=t,d&&d(),f;if(null===t)return d&&d(),t;if(r&&!c.iterateIn&&!c.iterateUnsetNumeric){var b=a.indexOf(t);if(!(b<0))return i[e]="#",d&&d({cyclicKeypath:s[b]}),"#"+s[b];!0===r&&(a.push(t),s.push(e))}var m,O=isPlainObject(t),T=isArray(t),P=(O||T)&&(!o.plainObjectReplacers.length||c.replaced)||c.iterateIn?t:replace(e,t,c,u,O||T,null,d);if(P!==t?(f=P,h={replaced:P}):T&&"object"!==c.iterateIn||"array"===c.iterateIn?(m=new Array(t.length),h={clone:m}):O||"object"===c.iterateIn?(m={},c.addLength&&(m.length=t.length),h={clone:m}):""===e&&hasConstructorOf(t,TypesonPromise)?(u.push([e,t,r,c,void 0,void 0,c.type]),f=t):f=t,d&&d(),n.iterateNone)return m||f;if(!m)return f;if(c.iterateIn){var g=function _loop(n){var o={ownKeys:hasOwn$1.call(t,n)};_adaptBuiltinStateObjectProperties(c,o,function(){var o=e+(e?".":"")+escapeKeyPathComponent(n),i=_encapsulate(o,t[n],!!r,c,u,p);hasConstructorOf(i,TypesonPromise)?u.push([o,i,!!r,c,m,n,c.type]):void 0!==i&&(m[n]=i)})};for(var _ in t)g(_);d&&d({endIterateIn:!0,end:!0})}else keys(t).forEach(function(n){var o=e+(e?".":"")+escapeKeyPathComponent(n);_adaptBuiltinStateObjectProperties(c,{ownKeys:!0},function(){var e=_encapsulate(o,t[n],!!r,c,u,p);hasConstructorOf(e,TypesonPromise)?u.push([o,e,!!r,c,m,n,c.type]):void 0!==e&&(m[n]=e)})}),d&&d({endIterateOwn:!0,end:!0});if(c.iterateUnsetNumeric){for(var S=t.length,j=function _loop2(n){if(!(n in t)){var o=e+(e?".":"")+n;_adaptBuiltinStateObjectProperties(c,{ownKeys:!1},function(){var e=_encapsulate(o,void 0,!!r,c,u,p);hasConstructorOf(e,TypesonPromise)?u.push([o,e,!!r,c,m,n,c.type]):void 0!==e&&(m[n]=e)})}},w=0;w<S;w++)j(w);d&&d({endIterateUnsetNumeric:!0,end:!0})}return m}function replace(e,t,n,a,s,c,y){for(var p=s?o.plainObjectReplacers:o.nonplainObjectReplacers,l=p.length;l--;){var f=p[l];if(f.test(t,n)){var h=f.type;if(o.revivers[h]){var v=i[e];i[e]=v?[h].concat(v):h}return Object.assign(n,{type:h,replaced:!0}),!r&&f.replaceAsync||f.replace?(y&&y({replacing:!0}),_encapsulate(e,f[r||!f.replaceAsync?"replace":"replaceAsync"](t,n),u&&"readonly",n,a,c,h)):(y&&y({typeDetected:!0}),_encapsulate(e,t,u&&"readonly",n,a,c,h))}}return t}return c.length?r&&n.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():Promise.resolve(checkPromises(p,c)).then(finish):!r&&n.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():n.stringification&&r?[finish(p)]:r?finish(p):Promise.resolve(finish(p))}},{key:"encapsulateSync",value:function encapsulateSync(e,t,n){return this.encapsulate(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!0}))}},{key:"encapsulateAsync",value:function encapsulateAsync(e,t,n){return this.encapsulate(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!1}))}},{key:"revive",value:function revive(e,t){var n=e&&e.$types;if(!n)return e;if(!0===n)return e.$;var r=(t=_objectSpread({sync:!0},this.options,t)).sync,o=[],i={},a=!0;n.$&&isPlainObject(n.$)&&(e=e.$,n=n.$,a=!1);var s=this;function _revive(e,t,c,u,y){if(!a||"$types"!==e){var p=n[e];if(isArray(t)||isPlainObject(t)){var l=isArray(t)?new Array(t.length):{};for(keys(t).forEach(function(n){var r=_revive(e+(e?".":"")+escapeKeyPathComponent(n),t[n],c||l,l,n);hasConstructorOf(r,Undefined)?l[n]=void 0:void 0!==r&&(l[n]=r)}),t=l;o.length;){var f=_slicedToArray(o[0],4),h=f[0],v=f[1],d=f[2],b=f[3],m=getByKeyPath(h,v);if(hasConstructorOf(m,Undefined))d[b]=void 0;else{if(void 0===m)break;d[b]=m}o.splice(0,1)}}if(!p)return t;if("#"===p){var O=getByKeyPath(c,t.slice(1));return void 0===O&&o.push([c,t.slice(1),u,y]),O}return[].concat(p).reduce(function reducer(e,t){if(hasConstructorOf(e,TypesonPromise))return e.then(function(e){return reducer(e,t)});var n=_slicedToArray(s.revivers[t],1)[0];if(!n)throw new Error("Unregistered type: "+t);return n[r&&n.revive?"revive":!r&&n.reviveAsync?"reviveAsync":"revive"](e,i)},t)}}function checkUndefined(e){return hasConstructorOf(e,Undefined)?void 0:e}var c,u=function revivePlainObjects(){var t=[];if(Object.entries(n).forEach(function(e){var r=_slicedToArray(e,2),o=r[0],i=r[1];"#"!==i&&[].concat(i).forEach(function(e){_slicedToArray(s.revivers[e],2)[1].plain&&(t.push({keypath:o,type:e}),delete n[o])})}),t.length)return t.sort(nestedPathsFirst).reduce(function reducer(t,n){var o=n.keypath,a=n.type;if(hasConstructorOf(t,TypesonPromise))return t.then(function(e){return reducer(e,a)});var c=getByKeyPath(e,o);if(hasConstructorOf(c,TypesonPromise))return c.then(function(e){return reducer(e,a)});var u=_slicedToArray(s.revivers[a],1)[0];if(!u)throw new Error("Unregistered type: "+a);void 0!==(c=u[r&&u.revive?"revive":!r&&u.reviveAsync?"reviveAsync":"revive"](c,i))&&(hasConstructorOf(c,Undefined)&&(c=void 0),setAtKeyPath(e,o,c)===c&&(e=c))},void 0)}();return isThenable(c=hasConstructorOf(u,TypesonPromise)?u.then(function(){return _revive("",e,null)}):_revive("",e,null))?r&&t.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():hasConstructorOf(c,TypesonPromise)?c.p.then(checkUndefined):c:!r&&t.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():r?checkUndefined(c):Promise.resolve(checkUndefined(c))}},{key:"reviveSync",value:function reviveSync(e,t){return this.revive(e,_objectSpread({throwOnBadSyncType:!0},t,{sync:!0}))}},{key:"reviveAsync",value:function reviveAsync(e,t){return this.revive(e,_objectSpread({throwOnBadSyncType:!0},t,{sync:!1}))}},{key:"register",value:function register(e,t){return t=t||{},[].concat(e).forEach(function R(e){if(isArray(e))return e.map(R,this);e&&keys(e).forEach(function(n){if("#"===n)throw new TypeError("# cannot be used as a type name as it is reserved for cyclic objects");if(Typeson.JSON_TYPES.includes(n))throw new TypeError("Plain JSON object types are reserved as type names");var r=e[n],o=r.testPlainObjects?this.plainObjectReplacers:this.nonplainObjectReplacers,i=o.filter(function(e){return e.type===n});if(i.length&&(o.splice(o.indexOf(i[0]),1),delete this.revivers[n],delete this.types[n]),r){if("function"==typeof r){var a=r;r={test:function test(e){return e&&e.constructor===a},replace:function replace(e){return Object.assign({},e)},revive:function revive(e){return Object.assign(Object.create(a.prototype),e)}}}else if(isArray(r)){var s=_slicedToArray(r,3);r={test:s[0],replace:s[1],revive:s[2]}}var c={type:n,test:r.test.bind(r)};r.replace&&(c.replace=r.replace.bind(r)),r.replaceAsync&&(c.replaceAsync=r.replaceAsync.bind(r));var u="number"==typeof t.fallback?t.fallback:t.fallback?0:1/0;if(r.testPlainObjects?this.plainObjectReplacers.splice(u,0,c):this.nonplainObjectReplacers.splice(u,0,c),r.revive||r.reviveAsync){var y={};r.revive&&(y.revive=r.revive.bind(r)),r.reviveAsync&&(y.reviveAsync=r.reviveAsync.bind(r)),this.revivers[n]=[y,{plain:r.testPlainObjects}]}this.types[n]=r}},this)},this),this}}]),Typeson}(),Undefined=function Undefined(){_classCallCheck(this,Undefined)};Typeson.Undefined=Undefined,Typeson.Promise=TypesonPromise,Typeson.isThenable=isThenable,Typeson.toStringTag=toStringTag,Typeson.hasConstructorOf=hasConstructorOf,Typeson.isObject=isObject,Typeson.isPlainObject=isPlainObject,Typeson.isUserObject=isUserObject,Typeson.escapeKeyPathComponent=escapeKeyPathComponent,Typeson.unescapeKeyPathComponent=unescapeKeyPathComponent,Typeson.getByKeyPath=getByKeyPath,Typeson.getJSONType=getJSONType,Typeson.JSON_TYPES=["null","boolean","number","string","array","object"],module.exports=Typeson; |
@@ -1,1 +0,1 @@ | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):e.Typeson=n()}(this,function(){"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function _slicedToArray(e,n){return function _arrayWithHoles(e){if(Array.isArray(e))return e}(e)||function _iterableToArrayLimit(e,n){var t=[],r=!0,o=!1,i=void 0;try{for(var s,a=e[Symbol.iterator]();!(r=(s=a.next()).done)&&(t.push(s.value),!n||t.length!==n);r=!0);}catch(e){o=!0,i=e}finally{try{r||null==a.return||a.return()}finally{if(o)throw i}}return t}(e,n)||function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function _toConsumableArray(e){return function _arrayWithoutHoles(e){if(Array.isArray(e)){for(var n=0,t=new Array(e.length);n<e.length;n++)t[n]=e[n];return t}}(e)||function _iterableToArray(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var e=Object.keys,n=Array.isArray,t={}.toString,r=Object.getPrototypeOf,o={}.hasOwnProperty,i=o.toString,s=["type","replaced","iterateIn","iterateUnsetNumeric"];function isThenable(e,n){return Typeson.isObject(e)&&"function"==typeof e.then&&(!n||"function"==typeof e.catch)}function toStringTag(e){return t.call(e).slice(8,-1)}function hasConstructorOf(e,n){if(!e||"object"!==_typeof(e))return!1;var t=r(e);if(!t)return!1;var s=o.call(t,"constructor")&&t.constructor;return"function"!=typeof s?null===n:"function"==typeof s&&null!==n&&i.call(s)===i.call(n)}function isPlainObject(e){return!(!e||"Object"!==toStringTag(e))&&(!r(e)||hasConstructorOf(e,Object))}function isObject(e){return e&&"object"===_typeof(e)}function Typeson(t){var r=[],o=[],i={},a=this.types={},c=this.stringify=function(e,r,o,i){i=Object.assign({},t,i,{stringification:!0});var s=y(e,null,i);return n(s)?JSON.stringify(s[0],r,o):s.then(function(e){return JSON.stringify(e,r,o)})};this.stringifySync=function(e,n,t,r){return c(e,n,t,Object.assign({},{throwOnBadSyncType:!0},r,{sync:!0}))},this.stringifyAsync=function(e,n,t,r){return c(e,n,t,Object.assign({},{throwOnBadSyncType:!0},r,{sync:!1}))};var u=this.parse=function(e,n,r){return r=Object.assign({},t,r,{parse:!0}),p(JSON.parse(e,n),r)};this.parseSync=function(e,n,t){return u(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!0}))},this.parseAsync=function(e,n,t){return u(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!1}))},this.specialTypeNames=function(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.returnTypeNames=!0,this.encapsulate(e,n,t)},this.rootTypeName=function(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.iterateNone=!0,this.encapsulate(e,n,t)};var y=this.encapsulate=function(a,c,u){var y=(u=Object.assign({sync:!0},t,u)).sync,p={},f=[],l=[],h=[],v=!(u&&"cyclic"in u)||u.cyclic,d=u.encapsulateObserver,b=_encapsulate("",a,v,c||{},h);function finish(e){var n=Object.values(p);if(u.iterateNone)return n.length?n[0]:Typeson.getJSONType(e);if(n.length){if(u.returnTypeNames)return _toConsumableArray(new Set(n));e&&isPlainObject(e)&&!e.hasOwnProperty("$types")?e.$types=p:e={$:e,$types:{$:p}}}else isObject(e)&&e.hasOwnProperty("$types")&&(e={$:e,$types:!0});return!u.returnTypeNames&&e}return h.length?y&&u.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():Promise.resolve(function checkPromises(e,n){return Promise.all(n.map(function(e){return e[1].p})).then(function(t){return Promise.all(t.map(function(t){var r=[],o=_slicedToArray(n.splice(0,1)[0],7),i=o[0],s=o[2],a=o[3],c=o[4],u=o[5],y=o[6],p=_encapsulate(i,t,s,a,r,!0,y),f=hasConstructorOf(p,TypesonPromise);return i&&f?p.p.then(function(n){return c[u]=n,checkPromises(e,r)}):(i?c[u]=p:e=f?p.p:p,checkPromises(e,r))}))}).then(function(){return e})}(b,h)).then(finish):!y&&u.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():u.stringification&&y?[finish(b)]:y?finish(b):Promise.resolve(finish(b));function _adaptBuiltinStateObjectProperties(e,n,t){Object.assign(e,n);var r=s.map(function(n){var t=e[n];return delete e[n],t});t(),s.forEach(function(n,t){e[n]=r[t]})}function _encapsulate(t,o,i,s,a,c,y){var h,v={},b=_typeof(o),m=d?function(e){var n=y||s.type||Typeson.getJSONType(o);d(Object.assign(e||v,{keypath:t,value:o,cyclic:i,stateObj:s,promisesData:a,resolvingTypesonPromise:c,awaitingTypesonPromise:hasConstructorOf(o,TypesonPromise)},void 0!==n?{type:n}:{}))}:null;if(b in{string:1,boolean:1,number:1,undefined:1})return void 0===o||"number"===b&&(isNaN(o)||o===-1/0||o===1/0)?(h=replace(t,o,s,a,!1,c,m))!==o&&(v={replaced:h}):h=o,m&&m(),h;if(null===o)return m&&m(),o;if(i&&!s.iterateIn&&!s.iterateUnsetNumeric){var O=f.indexOf(o);if(!(O<0))return p[t]="#",m&&m({cyclicKeypath:l[O]}),"#"+l[O];!0===i&&(f.push(o),l.push(t))}var T,g=isPlainObject(o),P=n(o),j=(g||P)&&(!r.length||s.replaced)||s.iterateIn?o:replace(t,o,s,a,g||P,null,m);if(j!==o?(h=j,v={replaced:j}):P||"array"===s.iterateIn?(T=new Array(o.length),v={clone:T}):g||"object"===s.iterateIn?v={clone:T={}}:""===t&&hasConstructorOf(o,TypesonPromise)?(a.push([t,o,i,s,void 0,void 0,s.type]),h=o):h=o,m&&m(),u.iterateNone)return T||h;if(!T)return h;if(s.iterateIn){var S=function _loop(e){var n={ownKeys:o.hasOwnProperty(e)};_adaptBuiltinStateObjectProperties(s,n,function(){var n=t+(t?".":"")+escapeKeyPathComponent(e),r=_encapsulate(n,o[e],!!i,s,a,c);hasConstructorOf(r,TypesonPromise)?a.push([n,r,!!i,s,T,e,s.type]):void 0!==r&&(T[e]=r)})};for(var w in o)S(w);m&&m({endIterateIn:!0,end:!0})}else e(o).forEach(function(e){var n=t+(t?".":"")+escapeKeyPathComponent(e);_adaptBuiltinStateObjectProperties(s,{ownKeys:!0},function(){var t=_encapsulate(n,o[e],!!i,s,a,c);hasConstructorOf(t,TypesonPromise)?a.push([n,t,!!i,s,T,e,s.type]):void 0!==t&&(T[e]=t)})}),m&&m({endIterateOwn:!0,end:!0});if(s.iterateUnsetNumeric){for(var A=o.length,_=function _loop2(e){if(!(e in o)){var n=t+(t?".":"")+e;_adaptBuiltinStateObjectProperties(s,{ownKeys:!1},function(){var t=_encapsulate(n,void 0,!!i,s,a,c);hasConstructorOf(t,TypesonPromise)?a.push([n,t,!!i,s,T,e,s.type]):void 0!==t&&(T[e]=t)})}},C=0;C<A;C++)_(C);m&&m({endIterateUnsetNumeric:!0,end:!0})}return T}function replace(e,n,t,s,a,c,u){for(var f=a?r:o,l=f.length;l--;){var h=f[l];if(h.test(n,t)){var d=h.type;if(i[d]){var b=p[e];p[e]=b?[d].concat(b):d}return Object.assign(t,{type:d,replaced:!0}),!y&&h.replaceAsync||h.replace?(u&&u({replacing:!0}),_encapsulate(e,h[y||!h.replaceAsync?"replace":"replaceAsync"](n,t),v&&"readonly",t,s,c,d)):(u&&u({typeDetected:!0}),_encapsulate(e,n,v&&"readonly",t,s,c,d))}}return n}};this.encapsulateSync=function(e,n,t){return y(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!0}))},this.encapsulateAsync=function(e,n,t){return y(e,n,Object.assign({},{throwOnBadSyncType:!0},t,{sync:!1}))};var p=this.revive=function(r,o){var s=(o=Object.assign({sync:!0},t,o)).sync,a=r&&r.$types,c=!0;if(!a)return r;if(!0===a)return r.$;a.$&&isPlainObject(a.$)&&(r=r.$,a=a.$,c=!1);var u=[],y={},p=function _revive(t,r,o,s,p,f){if(c&&"$types"===t)return;var l=a[t];if(n(r)||isPlainObject(r)){var h=n(r)?new Array(r.length):{};for(e(r).forEach(function(e){var n=_revive(t+(t?".":"")+escapeKeyPathComponent(e),r[e],o||h,s,h,e);hasConstructorOf(n,Undefined)?h[e]=void 0:void 0!==n&&(h[e]=n)}),r=h;u.length;){var v=_slicedToArray(u[0],4),d=v[0],b=v[1],m=v[2],O=v[3],T=getByKeyPath(d,b);if(hasConstructorOf(T,Undefined))m[O]=void 0;else{if(void 0===T)break;m[O]=T}u.splice(0,1)}}if(!l)return r;if("#"===l){var g=getByKeyPath(o,r.substr(1));return void 0===g&&u.push([o,r.substr(1),p,f]),g}var P=s.sync;return[].concat(l).reduce(function(e,n){var t=i[n];if(!t)throw new Error("Unregistered type: "+n);return t[P&&t.revive?"revive":!P&&t.reviveAsync?"reviveAsync":"revive"](e,y)},r)}("",r,null,o);return isThenable(p=hasConstructorOf(p,Undefined)?void 0:p)?s&&o.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():p:!s&&o.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():s?p:Promise.resolve(p)};this.reviveSync=function(e,n){return p(e,Object.assign({},{throwOnBadSyncType:!0},n,{sync:!0}))},this.reviveAsync=function(e,n){return p(e,Object.assign({},{throwOnBadSyncType:!0},n,{sync:!1}))},this.register=function(t,s){return s=s||{},[].concat(t).forEach(function R(t){if(n(t))return t.map(R);t&&e(t).forEach(function(e){if("#"===e)throw new TypeError("# cannot be used as a type name as it is reserved for cyclic objects");if(Typeson.JSON_TYPES.includes(e))throw new TypeError("Plain JSON object types are reserved as type names");var c=t[e],u=c.testPlainObjects?r:o,y=u.filter(function(n){return n.type===e});if(y.length&&(u.splice(u.indexOf(y[0]),1),delete i[e],delete a[e]),c){if("function"==typeof c){var p=c;c={test:function test(e){return e&&e.constructor===p},replace:function replace(e){return assign({},e)},revive:function revive(e){return assign(Object.create(p.prototype),e)}}}else n(c)&&(c={test:c[0],replace:c[1],revive:c[2]});var f={type:e,test:c.test.bind(c)};c.replace&&(f.replace=c.replace.bind(c)),c.replaceAsync&&(f.replaceAsync=c.replaceAsync.bind(c));var l="number"==typeof s.fallback?s.fallback:s.fallback?0:1/0;if(c.testPlainObjects?r.splice(l,0,f):o.splice(l,0,f),c.revive||c.reviveAsync){var h={};c.revive&&(h.revive=c.revive.bind(c)),c.reviveAsync&&(h.reviveAsync=c.reviveAsync.bind(c)),i[e]=h}a[e]=c}})}),this}}function assign(n,t){return e(t).map(function(e){n[e]=t[e]}),n}function escapeKeyPathComponent(e){return e.replace(/~/g,"~0").replace(/\./g,"~1")}function unescapeKeyPathComponent(e){return e.replace(/~1/g,".").replace(/~0/g,"~")}function getByKeyPath(e,n){if(""===n)return e;var t=n.indexOf(".");if(t>-1){var r=e[unescapeKeyPathComponent(n.substr(0,t))];return void 0===r?void 0:getByKeyPath(r,n.substr(t+1))}return e[unescapeKeyPathComponent(n)]}function Undefined(){}function TypesonPromise(e){this.p=new Promise(e)}return"undefined"!=typeof Symbol&&(TypesonPromise.prototype[Symbol.toStringTag]="TypesonPromise"),TypesonPromise.prototype.then=function(e,n){var t=this;return new TypesonPromise(function(r,o){t.p.then(function(n){r(e?e(n):n)},function(e){t.p.catch(function(e){return n?n(e):Promise.reject(e)}).then(r,o)})})},TypesonPromise.prototype.catch=function(e){return this.then(null,e)},TypesonPromise.resolve=function(e){return new TypesonPromise(function(n){n(e)})},TypesonPromise.reject=function(e){return new TypesonPromise(function(n,t){t(e)})},["all","race"].map(function(e){TypesonPromise[e]=function(n){return new TypesonPromise(function(t,r){Promise[e](n.map(function(e){return e.p})).then(t,r)})}}),Typeson.Undefined=Undefined,Typeson.Promise=TypesonPromise,Typeson.isThenable=isThenable,Typeson.toStringTag=toStringTag,Typeson.hasConstructorOf=hasConstructorOf,Typeson.isObject=isObject,Typeson.isPlainObject=isPlainObject,Typeson.isUserObject=function isUserObject(e){if(!e||"Object"!==toStringTag(e))return!1;var n=r(e);return!n||hasConstructorOf(e,Object)||isUserObject(n)},Typeson.escapeKeyPathComponent=escapeKeyPathComponent,Typeson.unescapeKeyPathComponent=unescapeKeyPathComponent,Typeson.getByKeyPath=getByKeyPath,Typeson.getJSONType=function(e){return null===e?"null":n(e)?"array":_typeof(e)},Typeson.JSON_TYPES=["null","boolean","number","string","array","object"],Typeson}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Typeson=t()}(this,function(){"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function asyncGeneratorStep(e,t,n,r,i,a,o){try{var c=e[a](o),s=c.value}catch(e){return void n(e)}c.done?t(s):Promise.resolve(s).then(r,i)}function _asyncToGenerator(e){return function(){var t=this,n=arguments;return new Promise(function(r,i){var a=e.apply(t,n);function _next(e){asyncGeneratorStep(a,r,i,_next,_throw,"next",e)}function _throw(e){asyncGeneratorStep(a,r,i,_next,_throw,"throw",e)}_next(void 0)})}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function _objectSpread(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){_defineProperty(e,t,n[t])})}return e}function _slicedToArray(e,t){return function _arrayWithHoles(e){if(Array.isArray(e))return e}(e)||function _iterableToArrayLimit(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o,c=e[Symbol.iterator]();!(r=(o=c.next()).done)&&(n.push(o.value),!t||n.length!==t);r=!0);}catch(e){i=!0,a=e}finally{try{r||null==c.return||c.return()}finally{if(i)throw a}}return n}(e,t)||function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function _toConsumableArray(e){return function _arrayWithoutHoles(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t<e.length;t++)n[t]=e[t];return n}}(e)||function _iterableToArray(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var e=function TypesonPromise(e){_classCallCheck(this,TypesonPromise),this.p=new Promise(e)};"undefined"!=typeof Symbol&&(e.prototype[Symbol.toStringTag]="TypesonPromise"),e.prototype.then=function(t,n){var r=this;return new e(function(e,i){r.p.then(function(n){e(t?t(n):n)},function(t){r.p.catch(function(e){return n?n(e):Promise.reject(e)}).then(e,i)})})},e.prototype.catch=function(e){return this.then(null,e)},e.resolve=function(t){return new e(function(e){e(t)})},e.reject=function(t){return new e(function(e,n){n(t)})},["all","race"].map(function(t){e[t]=function(n){return new e(function(e,r){Promise[t](n.map(function(e){return e.p})).then(e,r)})}});var t={}.toString,n={}.hasOwnProperty,r=Object.getPrototypeOf,i=n.toString;function isThenable(e,t){return isObject(e)&&"function"==typeof e.then&&(!t||"function"==typeof e.catch)}function toStringTag(e){return t.call(e).slice(8,-1)}function hasConstructorOf(e,t){if(!e||"object"!==_typeof(e))return!1;var a=r(e);if(!a)return!1;var o=n.call(a,"constructor")&&a.constructor;return"function"!=typeof o?null===t:"function"==typeof o&&null!==t&&i.call(o)===i.call(t)}function isPlainObject(e){return!(!e||"Object"!==toStringTag(e))&&(!r(e)||hasConstructorOf(e,Object))}function isObject(e){return e&&"object"===_typeof(e)}function escapeKeyPathComponent(e){return e.replace(/~/g,"~0").replace(/\./g,"~1")}function unescapeKeyPathComponent(e){return e.replace(/~1/g,".").replace(/~0/g,"~")}function getByKeyPath(e,t){if(""===t)return e;var n=t.indexOf(".");if(n>-1){var r=e[unescapeKeyPathComponent(t.substr(0,n))];return void 0===r?void 0:getByKeyPath(r,t.substr(n+1))}return e[unescapeKeyPathComponent(t)]}var a=Object.keys,o=Array.isArray,c={}.hasOwnProperty,s=["type","replaced","iterateIn","iterateUnsetNumeric"];function nestedPathsFirst(e,t){var n=e.keypath.match(/\./g),r=e.keypath.match(/\./g);return n&&(n=n.length),r&&(r=r.length),n>r?-1:r<n?1:e.keypath<t.keypath?-1:e.keypath>t.keypath}var u=function(){function Typeson(e){_classCallCheck(this,Typeson),this.options=e,this.plainObjectReplacers=[],this.nonplainObjectReplacers=[],this.revivers={},this.types={}}return function _createClass(e,t,n){return t&&_defineProperties(e.prototype,t),n&&_defineProperties(e,n),e}(Typeson,[{key:"stringify",value:function stringify(e,t,n,r){r=_objectSpread({},this.options,r,{stringification:!0});var i=this.encapsulate(e,null,r);return o(i)?JSON.stringify(i[0],t,n):i.then(function(e){return JSON.stringify(e,t,n)})}},{key:"stringifySync",value:function stringifySync(e,t,n,r){return this.stringify(e,t,n,_objectSpread({throwOnBadSyncType:!0},r,{sync:!0}))}},{key:"stringifyAsync",value:function stringifyAsync(e,t,n,r){return this.stringify(e,t,n,_objectSpread({throwOnBadSyncType:!0},r,{sync:!1}))}},{key:"parse",value:function parse(e,t,n){return n=_objectSpread({},this.options,n,{parse:!0}),this.revive(JSON.parse(e,t),n)}},{key:"parseSync",value:function parseSync(e,t,n){return this.parse(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!0}))}},{key:"parseAsync",value:function parseAsync(e,t,n){return this.parse(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!1}))}},{key:"specialTypeNames",value:function specialTypeNames(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return n.returnTypeNames=!0,this.encapsulate(e,t,n)}},{key:"rootTypeName",value:function rootTypeName(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return n.iterateNone=!0,this.encapsulate(e,t,n)}},{key:"encapsulate",value:function encapsulate(t,n,r){var i=(r=_objectSpread({sync:!0},this.options,r)).sync,u=this,p={},l=[],y=[],f=[],h=!(r&&"cyclic"in r)||r.cyclic,v=r.encapsulateObserver,d=_encapsulate("",t,h,n||{},f);function finish(e){var t=Object.values(p);if(r.iterateNone)return t.length?t[0]:Typeson.getJSONType(e);if(t.length){if(r.returnTypeNames)return _toConsumableArray(new Set(t));e&&isPlainObject(e)&&!c.call(e,"$types")?e.$types=p:e={$:e,$types:{$:p}}}else isObject(e)&&c.call(e,"$types")&&(e={$:e,$types:!0});return!r.returnTypeNames&&e}function checkPromises(e,t){return _checkPromises.apply(this,arguments)}function _checkPromises(){return(_checkPromises=_asyncToGenerator(regeneratorRuntime.mark(function _callee2(t,n){var r;return regeneratorRuntime.wrap(function _callee2$(i){for(;;)switch(i.prev=i.next){case 0:return i.next=2,Promise.all(n.map(function(e){return e[1].p}));case 2:return r=i.sent,i.next=5,Promise.all(r.map(function(){var r=_asyncToGenerator(regeneratorRuntime.mark(function _callee(r){var i,a,o,c,s,u,p,l,y,f,h,v,d,b;return regeneratorRuntime.wrap(function _callee$(O){for(;;)switch(O.prev=O.next){case 0:if(i=[],a=n.splice(0,1),o=_slicedToArray(a,1),c=o[0],s=_slicedToArray(c,7),u=s[0],p=s[2],l=s[3],y=s[4],f=s[5],h=s[6],v=_encapsulate(u,r,p,l,i,!0,h),d=hasConstructorOf(v,e),!u||!d){O.next=11;break}return O.next=8,v.p;case 8:return b=O.sent,y[f]=b,O.abrupt("return",checkPromises(t,i));case 11:return u?y[f]=v:t=d?v.p:v,O.abrupt("return",checkPromises(t,i));case 13:case"end":return O.stop()}},_callee,this)}));return function(e){return r.apply(this,arguments)}}()));case 5:return i.abrupt("return",t);case 6:case"end":return i.stop()}},_callee2,this)}))).apply(this,arguments)}function _adaptBuiltinStateObjectProperties(e,t,n){Object.assign(e,t);var r=s.map(function(t){var n=e[t];return delete e[t],n});n(),s.forEach(function(t,n){e[t]=r[n]})}function _encapsulate(t,n,i,s,f,h,d){var b,O={},m=_typeof(n),g=v?function(r){var a=d||s.type||Typeson.getJSONType(n);v(Object.assign(r||O,{keypath:t,value:n,cyclic:i,stateObj:s,promisesData:f,resolvingTypesonPromise:h,awaitingTypesonPromise:hasConstructorOf(n,e)},void 0!==a?{type:a}:{}))}:null;if(["string","boolean","number","undefined"].includes(m))return void 0===n||"number"===m&&(isNaN(n)||n===-1/0||n===1/0)?(b=replace(t,n,s,f,!1,h,g))!==n&&(O={replaced:b}):b=n,g&&g(),b;if(null===n)return g&&g(),n;if(i&&!s.iterateIn&&!s.iterateUnsetNumeric){var _=l.indexOf(n);if(!(_<0))return p[t]="#",g&&g({cyclicKeypath:y[_]}),"#"+y[_];!0===i&&(l.push(n),y.push(t))}var j,P=isPlainObject(n),S=o(n),T=(P||S)&&(!u.plainObjectReplacers.length||s.replaced)||s.iterateIn?n:replace(t,n,s,f,P||S,null,g);if(T!==n?(b=T,O={replaced:T}):S&&"object"!==s.iterateIn||"array"===s.iterateIn?(j=new Array(n.length),O={clone:j}):P||"object"===s.iterateIn?(j={},s.addLength&&(j.length=n.length),O={clone:j}):""===t&&hasConstructorOf(n,e)?(f.push([t,n,i,s,void 0,void 0,s.type]),b=n):b=n,g&&g(),r.iterateNone)return j||b;if(!j)return b;if(s.iterateIn){var w=function _loop(r){var a={ownKeys:c.call(n,r)};_adaptBuiltinStateObjectProperties(s,a,function(){var a=t+(t?".":"")+escapeKeyPathComponent(r),o=_encapsulate(a,n[r],!!i,s,f,h);hasConstructorOf(o,e)?f.push([a,o,!!i,s,j,r,s.type]):void 0!==o&&(j[r]=o)})};for(var A in n)w(A);g&&g({endIterateIn:!0,end:!0})}else a(n).forEach(function(r){var a=t+(t?".":"")+escapeKeyPathComponent(r);_adaptBuiltinStateObjectProperties(s,{ownKeys:!0},function(){var t=_encapsulate(a,n[r],!!i,s,f,h);hasConstructorOf(t,e)?f.push([a,t,!!i,s,j,r,s.type]):void 0!==t&&(j[r]=t)})}),g&&g({endIterateOwn:!0,end:!0});if(s.iterateUnsetNumeric){for(var k=n.length,C=function _loop2(r){if(!(r in n)){var a=t+(t?".":"")+r;_adaptBuiltinStateObjectProperties(s,{ownKeys:!1},function(){var t=_encapsulate(a,void 0,!!i,s,f,h);hasConstructorOf(t,e)?f.push([a,t,!!i,s,j,r,s.type]):void 0!==t&&(j[r]=t)})}},K=0;K<k;K++)C(K);g&&g({endIterateUnsetNumeric:!0,end:!0})}return j}function replace(e,t,n,r,a,o,c){for(var s=a?u.plainObjectReplacers:u.nonplainObjectReplacers,l=s.length;l--;){var y=s[l];if(y.test(t,n)){var f=y.type;if(u.revivers[f]){var v=p[e];p[e]=v?[f].concat(v):f}return Object.assign(n,{type:f,replaced:!0}),!i&&y.replaceAsync||y.replace?(c&&c({replacing:!0}),_encapsulate(e,y[i||!y.replaceAsync?"replace":"replaceAsync"](t,n),h&&"readonly",n,r,o,f)):(c&&c({typeDetected:!0}),_encapsulate(e,t,h&&"readonly",n,r,o,f))}}return t}return f.length?i&&r.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():Promise.resolve(checkPromises(d,f)).then(finish):!i&&r.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():r.stringification&&i?[finish(d)]:i?finish(d):Promise.resolve(finish(d))}},{key:"encapsulateSync",value:function encapsulateSync(e,t,n){return this.encapsulate(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!0}))}},{key:"encapsulateAsync",value:function encapsulateAsync(e,t,n){return this.encapsulate(e,t,_objectSpread({throwOnBadSyncType:!0},n,{sync:!1}))}},{key:"revive",value:function revive(t,n){var r=t&&t.$types;if(!r)return t;if(!0===r)return t.$;var i=(n=_objectSpread({sync:!0},this.options,n)).sync,c=[],s={},u=!0;r.$&&isPlainObject(r.$)&&(t=t.$,r=r.$,u=!1);var l=this;function _revive(t,n,y,f,h){if(!u||"$types"!==t){var v=r[t];if(o(n)||isPlainObject(n)){var d=o(n)?new Array(n.length):{};for(a(n).forEach(function(e){var r=_revive(t+(t?".":"")+escapeKeyPathComponent(e),n[e],y||d,d,e);hasConstructorOf(r,p)?d[e]=void 0:void 0!==r&&(d[e]=r)}),n=d;c.length;){var b=_slicedToArray(c[0],4),O=b[0],m=b[1],g=b[2],_=b[3],j=getByKeyPath(O,m);if(hasConstructorOf(j,p))g[_]=void 0;else{if(void 0===j)break;g[_]=j}c.splice(0,1)}}if(!v)return n;if("#"===v){var P=getByKeyPath(y,n.slice(1));return void 0===P&&c.push([y,n.slice(1),f,h]),P}return[].concat(v).reduce(function reducer(t,n){if(hasConstructorOf(t,e))return t.then(function(e){return reducer(e,n)});var r=_slicedToArray(l.revivers[n],1)[0];if(!r)throw new Error("Unregistered type: "+n);return r[i&&r.revive?"revive":!i&&r.reviveAsync?"reviveAsync":"revive"](t,s)},n)}}function checkUndefined(e){return hasConstructorOf(e,p)?void 0:e}var y,f=function revivePlainObjects(){var n=[];if(Object.entries(r).forEach(function(e){var t=_slicedToArray(e,2),i=t[0],a=t[1];"#"!==a&&[].concat(a).forEach(function(e){_slicedToArray(l.revivers[e],2)[1].plain&&(n.push({keypath:i,type:e}),delete r[i])})}),n.length)return n.sort(nestedPathsFirst).reduce(function reducer(n,r){var a=r.keypath,o=r.type;if(hasConstructorOf(n,e))return n.then(function(e){return reducer(e,o)});var c=getByKeyPath(t,a);if(hasConstructorOf(c,e))return c.then(function(e){return reducer(e,o)});var u=_slicedToArray(l.revivers[o],1)[0];if(!u)throw new Error("Unregistered type: "+o);void 0!==(c=u[i&&u.revive?"revive":!i&&u.reviveAsync?"reviveAsync":"revive"](c,s))&&(hasConstructorOf(c,p)&&(c=void 0),function setAtKeyPath(e,t,n){if(""===t)return n;var r=t.indexOf(".");return r>-1?setAtKeyPath(e[unescapeKeyPathComponent(t.substr(0,r))],t.substr(r+1),n):(e[unescapeKeyPathComponent(t)]=n,e)}(t,a,c)===c&&(t=c))},void 0)}();return isThenable(y=hasConstructorOf(f,e)?f.then(function(){return _revive("",t,null)}):_revive("",t,null))?i&&n.throwOnBadSyncType?function(){throw new TypeError("Sync method requested but async result obtained")}():hasConstructorOf(y,e)?y.p.then(checkUndefined):y:!i&&n.throwOnBadSyncType?function(){throw new TypeError("Async method requested but sync result obtained")}():i?checkUndefined(y):Promise.resolve(checkUndefined(y))}},{key:"reviveSync",value:function reviveSync(e,t){return this.revive(e,_objectSpread({throwOnBadSyncType:!0},t,{sync:!0}))}},{key:"reviveAsync",value:function reviveAsync(e,t){return this.revive(e,_objectSpread({throwOnBadSyncType:!0},t,{sync:!1}))}},{key:"register",value:function register(e,t){return t=t||{},[].concat(e).forEach(function R(e){if(o(e))return e.map(R,this);e&&a(e).forEach(function(n){if("#"===n)throw new TypeError("# cannot be used as a type name as it is reserved for cyclic objects");if(Typeson.JSON_TYPES.includes(n))throw new TypeError("Plain JSON object types are reserved as type names");var r=e[n],i=r.testPlainObjects?this.plainObjectReplacers:this.nonplainObjectReplacers,a=i.filter(function(e){return e.type===n});if(a.length&&(i.splice(i.indexOf(a[0]),1),delete this.revivers[n],delete this.types[n]),r){if("function"==typeof r){var c=r;r={test:function test(e){return e&&e.constructor===c},replace:function replace(e){return Object.assign({},e)},revive:function revive(e){return Object.assign(Object.create(c.prototype),e)}}}else if(o(r)){var s=_slicedToArray(r,3);r={test:s[0],replace:s[1],revive:s[2]}}var u={type:n,test:r.test.bind(r)};r.replace&&(u.replace=r.replace.bind(r)),r.replaceAsync&&(u.replaceAsync=r.replaceAsync.bind(r));var p="number"==typeof t.fallback?t.fallback:t.fallback?0:1/0;if(r.testPlainObjects?this.plainObjectReplacers.splice(p,0,u):this.nonplainObjectReplacers.splice(p,0,u),r.revive||r.reviveAsync){var l={};r.revive&&(l.revive=r.revive.bind(r)),r.reviveAsync&&(l.reviveAsync=r.reviveAsync.bind(r)),this.revivers[n]=[l,{plain:r.testPlainObjects}]}this.types[n]=r}},this)},this),this}}]),Typeson}(),p=function Undefined(){_classCallCheck(this,Undefined)};return u.Undefined=p,u.Promise=e,u.isThenable=isThenable,u.toStringTag=toStringTag,u.hasConstructorOf=hasConstructorOf,u.isObject=isObject,u.isPlainObject=isPlainObject,u.isUserObject=function isUserObject(e){if(!e||"Object"!==toStringTag(e))return!1;var t=r(e);return!t||hasConstructorOf(e,Object)||isUserObject(t)},u.escapeKeyPathComponent=escapeKeyPathComponent,u.unescapeKeyPathComponent=unescapeKeyPathComponent,u.getByKeyPath=getByKeyPath,u.getJSONType=function getJSONType(e){return null===e?"null":Array.isArray(e)?"array":_typeof(e)},u.JSON_TYPES=["null","boolean","number","string","array","object"],u}); |
{ | ||
"name": "typeson", | ||
"version": "5.10.1", | ||
"version": "5.11.0", | ||
"description": "Preserves types over JSON, BSON or socket.io", | ||
@@ -9,4 +9,6 @@ "main": "./dist/typeson-commonjs2.js", | ||
"scripts": { | ||
"eslint": "eslint .", | ||
"browser-test": "npm run eslint && npm run rollup && npm start test/", | ||
"prepublishOnly": "yarn", | ||
"eslint": "eslint --report-unused-disable-directives .", | ||
"start": "static -p 8092", | ||
"browser-test": "npm run eslint && npm run rollup && opn http://localhost:8092/test/ && npm start", | ||
"node-test": "npx babel-node test/test.js", | ||
@@ -41,5 +43,5 @@ "test": "npm run eslint && npm run rollup && npm run node-test", | ||
"devDependencies": { | ||
"@babel/core": "^7.1.6", | ||
"@babel/node": "^7.0.0", | ||
"@babel/preset-env": "^7.1.6", | ||
"@babel/core": "^7.2.0", | ||
"@babel/node": "^7.2.0", | ||
"@babel/preset-env": "^7.2.0", | ||
"base64-arraybuffer-es6": "^0.4.2", | ||
@@ -54,4 +56,4 @@ "eslint": "5.9.0", | ||
"node-static": "0.7.11", | ||
"opn": "5.4.0", | ||
"rollup": "0.67.3", | ||
"opn-cli": "^4.0.0", | ||
"rollup": "0.67.4", | ||
"rollup-plugin-babel": "^4.0.3", | ||
@@ -58,0 +60,0 @@ "rollup-plugin-node-resolve": "^3.4.0", |
1242
typeson.js
@@ -0,69 +1,42 @@ | ||
/** | ||
* Typeson - JSON with types | ||
* @license The MIT License (MIT) | ||
* @copyright (c) 2016-2018 David Fahlander, Brett Zamir | ||
*/ | ||
import {TypesonPromise} from './utils/TypesonPromise.js'; | ||
import { | ||
isPlainObject, isObject, hasConstructorOf, | ||
isThenable, toStringTag, isUserObject, | ||
escapeKeyPathComponent, unescapeKeyPathComponent, | ||
getByKeyPath, setAtKeyPath, getJSONType | ||
} from './utils/classMethods.js'; | ||
const {keys} = Object, | ||
{isArray} = Array, | ||
{toString} = {}, | ||
getProto = Object.getPrototypeOf, | ||
hasOwn = ({}.hasOwnProperty), | ||
fnToString = hasOwn.toString, | ||
internalStateObjPropsToIgnore = ['type', 'replaced', 'iterateIn', 'iterateUnsetNumeric']; | ||
internalStateObjPropsToIgnore = [ | ||
'type', 'replaced', 'iterateIn', 'iterateUnsetNumeric' | ||
]; | ||
function isThenable (v, catchCheck) { | ||
return Typeson.isObject(v) && typeof v.then === 'function' && (!catchCheck || typeof v.catch === 'function'); | ||
} | ||
function toStringTag (val) { | ||
return toString.call(val).slice(8, -1); | ||
} | ||
// This function is dependent on both constructors | ||
// being identical so any minimization is expected of both | ||
function hasConstructorOf (a, b) { | ||
if (!a || typeof a !== 'object') { | ||
return false; | ||
function nestedPathsFirst (a, b) { | ||
let as = a.keypath.match(/\./g); | ||
let bs = a.keypath.match(/\./g); | ||
if (as) { | ||
as = as.length; | ||
} | ||
const proto = getProto(a); | ||
if (!proto) { | ||
return false; | ||
if (bs) { | ||
bs = bs.length; | ||
} | ||
const Ctor = hasOwn.call(proto, 'constructor') && proto.constructor; | ||
if (typeof Ctor !== 'function') { | ||
return b === null; | ||
} | ||
return typeof Ctor === 'function' && b !== null && fnToString.call(Ctor) === fnToString.call(b); | ||
return as > bs | ||
? -1 | ||
: bs < as | ||
? 1 | ||
: a.keypath < b.keypath | ||
? -1 | ||
: a.keypath > b.keypath; | ||
} | ||
function isPlainObject (val) { // Mirrors jQuery's | ||
if (!val || toStringTag(val) !== 'Object') { | ||
return false; | ||
} | ||
const proto = getProto(val); | ||
if (!proto) { // `Object.create(null)` | ||
return true; | ||
} | ||
return hasConstructorOf(val, Object); | ||
} | ||
function isUserObject (val) { | ||
if (!val || toStringTag(val) !== 'Object') { | ||
return false; | ||
} | ||
const proto = getProto(val); | ||
if (!proto) { // `Object.create(null)` | ||
return true; | ||
} | ||
return hasConstructorOf(val, Object) || isUserObject(proto); | ||
} | ||
function isObject (v) { | ||
return v && typeof v === 'object'; | ||
} | ||
/* 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(). | ||
/** | ||
* An instance of this class can be used to call `stringify()` and `parse()`. | ||
* Typeson resolves cyclic references by default. Can also be extended to | ||
@@ -73,21 +46,35 @@ * support custom types using the register() method. | ||
* @constructor | ||
* @param {{cyclic: boolean}} [options] - if cyclic (default true), cyclic references will be handled gracefully. | ||
* @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()] | ||
const plainObjectReplacers = []; | ||
const nonplainObjectReplacers = []; | ||
// Revivers: map {type => reviver}. Sample: {'Date': value => new Date(value)} | ||
const revivers = {}; | ||
class Typeson { | ||
constructor (options) { | ||
this.options = options; | ||
/** Types registered via register() */ | ||
const regTypes = this.types = {}; | ||
// Replacers signature: replace (value). Returns falsy if not | ||
// replacing. Otherwise ['Date', value.getTime()] | ||
this.plainObjectReplacers = []; | ||
this.nonplainObjectReplacers = []; | ||
/** Serialize given object to Typeson. | ||
* | ||
* Arguments works identical to those of JSON.stringify(). | ||
// Revivers: [{type => reviver}, {plain: boolean}]. | ||
// Sample: [{'Date': value => new Date(value)}, {plain: false}] | ||
this.revivers = {}; | ||
/** Types registered via register() */ | ||
this.types = {}; | ||
} | ||
/** | ||
* Serialize given object to Typeson. | ||
* Initial arguments work identical to those of `JSON.stringify`. | ||
* The `replacer` argument has nothing to do with our replacers. | ||
* @param {*} obj | ||
* @param {function|string[]} replacer | ||
* @param {number|string} space | ||
* @param {object} opts | ||
* @returns {string|Promise} Promise resolves to a string | ||
*/ | ||
const stringify = this.stringify = function (obj, replacer, space, opts) { // replacer here has nothing to do with our replacers. | ||
opts = Object.assign({}, options, opts, {stringification: true}); | ||
const encapsulated = encapsulate(obj, null, opts); | ||
stringify (obj, replacer, space, opts) { | ||
opts = {...this.options, ...opts, stringification: true}; | ||
const encapsulated = this.encapsulate(obj, null, opts); | ||
if (isArray(encapsulated)) { | ||
@@ -99,57 +86,137 @@ return JSON.stringify(encapsulated[0], replacer, space); | ||
}); | ||
}; | ||
} | ||
// Also sync but throws on non-sync result | ||
this.stringifySync = function (obj, replacer, space, opts) { | ||
return stringify(obj, replacer, space, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: true})); | ||
}; | ||
this.stringifyAsync = function (obj, replacer, space, opts) { | ||
return stringify(obj, replacer, space, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: false})); | ||
}; | ||
/** | ||
* Also sync but throws on non-sync result | ||
* @param {*} obj | ||
* @param {function|string[]} replacer | ||
* @param {number|string} space | ||
* @param {object} opts | ||
* @returns {string} | ||
*/ | ||
stringifySync (obj, replacer, space, opts) { | ||
return this.stringify(obj, replacer, space, { | ||
throwOnBadSyncType: true, ...opts, sync: true | ||
}); | ||
} | ||
/** Parse Typeson back into an obejct. | ||
/** | ||
* | ||
* Arguments works identical to those of JSON.parse(). | ||
* @param {*} obj | ||
* @param {function|string[]} replacer | ||
* @param {number|string} space | ||
* @param {object} opts | ||
* @returns {Promise} Resolves to string | ||
*/ | ||
const parse = this.parse = function (text, reviver, opts) { | ||
opts = Object.assign({}, options, opts, {parse: true}); | ||
return revive(JSON.parse(text, reviver), opts); // This reviver has nothing to do with our revivers. | ||
}; | ||
stringifyAsync (obj, replacer, space, opts) { | ||
return this.stringify(obj, replacer, space, { | ||
throwOnBadSyncType: true, ...opts, sync: false | ||
}); | ||
} | ||
// Also sync but throws on non-sync result | ||
this.parseSync = function (text, reviver, opts) { | ||
return parse(text, reviver, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: true})); // This reviver has nothing to do with our revivers. | ||
}; | ||
this.parseAsync = function (text, reviver, opts) { | ||
return parse(text, reviver, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: false})); // This reviver has nothing to do with our revivers. | ||
}; | ||
/** | ||
* Parse Typeson back into an obejct. | ||
* Initial arguments works identical to those of `JSON.parse()`. | ||
* @param {string} text | ||
* @param {function} reviver This JSON reviver has nothing to do with | ||
* our revivers. | ||
* @param {object} opts | ||
* @returns {external:JSON} | ||
*/ | ||
parse (text, reviver, opts) { | ||
opts = {...this.options, ...opts, parse: true}; | ||
return this.revive(JSON.parse(text, reviver), opts); | ||
} | ||
this.specialTypeNames = function (obj, stateObj, opts = {}) { | ||
/** | ||
* Also sync but throws on non-sync result | ||
* @param {string} text | ||
* @param {function} reviver This JSON reviver has nothing to do with | ||
* our revivers. | ||
* @param {object} opts | ||
* @returns {external:JSON} | ||
*/ | ||
parseSync (text, reviver, opts) { | ||
return this.parse( | ||
text, | ||
reviver, | ||
{throwOnBadSyncType: true, ...opts, sync: true} | ||
); | ||
} | ||
/** | ||
* @param {string} text | ||
* @param {function} reviver This JSON reviver has nothing to do with | ||
* our revivers. | ||
* @param {object} opts | ||
* @returns {Promise} Resolves to `external:JSON` | ||
*/ | ||
parseAsync (text, reviver, opts) { | ||
return this.parse( | ||
text, | ||
reviver, | ||
{throwOnBadSyncType: true, ...opts, sync: false} | ||
); | ||
} | ||
/** | ||
* | ||
* @param {*} obj | ||
* @param {object} stateObj | ||
* @param {object} [opts={}] | ||
* @returns {string[]|false} | ||
*/ | ||
specialTypeNames (obj, stateObj, opts = {}) { | ||
opts.returnTypeNames = true; | ||
return this.encapsulate(obj, stateObj, opts); | ||
}; | ||
this.rootTypeName = function (obj, stateObj, opts = {}) { | ||
} | ||
/** | ||
* | ||
* @param {*} obj | ||
* @param {object} stateObj | ||
* @param {object} [opts={}] | ||
* @returns {Promise|Array|object|string|false} | ||
*/ | ||
rootTypeName (obj, stateObj, opts = {}) { | ||
opts.iterateNone = true; | ||
return this.encapsulate(obj, stateObj, opts); | ||
}; | ||
} | ||
/** Encapsulate a complex object into a plain Object by replacing registered types with | ||
* plain objects representing the types data. | ||
/** | ||
* Encapsulate a complex object into a plain Object by replacing | ||
* registered types with plain objects representing the types data. | ||
* | ||
* This method is used internally by Typeson.stringify(). | ||
* This method is used internally by T`ypeson.stringify()`. | ||
* @param {Object} obj - Object to encapsulate. | ||
* @param {object} stateObj | ||
* @param {object} opts | ||
* @returns {Promise|Array|object|string|false} | ||
*/ | ||
const encapsulate = this.encapsulate = function (obj, stateObj, opts) { | ||
opts = Object.assign({sync: true}, options, opts); | ||
encapsulate (obj, stateObj, opts) { | ||
opts = {sync: true, ...this.options, ...opts}; | ||
const {sync} = opts; | ||
const types = {}, | ||
const that = this, | ||
types = {}, | ||
refObjs = [], // For checking cyclic references | ||
refKeys = [], // For checking cyclic references | ||
promisesDataRoot = []; | ||
// Clone the object deeply while at the same time replacing any special types or cyclic reference: | ||
// Clone the object deeply while at the same time replacing any | ||
// special types or cyclic reference: | ||
const cyclic = opts && ('cyclic' in opts) ? opts.cyclic : true; | ||
const {encapsulateObserver} = opts; | ||
const ret = _encapsulate('', obj, cyclic, stateObj || {}, promisesDataRoot); | ||
const ret = _encapsulate( | ||
'', obj, cyclic, stateObj || {}, | ||
promisesDataRoot | ||
); | ||
/** | ||
* | ||
* @param {*} ret | ||
* @returns {Array|object|string|false} | ||
*/ | ||
function finish (ret) { | ||
// Add $types to result only if we ever bumped into a special type (or special case where object has own `$types`) | ||
// Add `$types` to result only if we ever bumped into a | ||
// special type (or special case where object has own `$types`) | ||
const typeNames = Object.values(types); | ||
@@ -166,4 +233,9 @@ if (opts.iterateNone) { | ||
} | ||
if (!ret || !isPlainObject(ret) || // Special if array (or a primitive) was serialized because JSON would ignore custom `$types` prop on it | ||
ret.hasOwnProperty('$types') // Also need to handle if this is an object with its own `$types` property (to avoid ambiguity) | ||
// Special if array (or a primitive) was serialized | ||
// because JSON would ignore custom `$types` prop on it | ||
if (!ret || !isPlainObject(ret) || | ||
// Also need to handle if this is an object with its | ||
// own `$types` property (to avoid ambiguity) | ||
hasOwn.call(ret, '$types') | ||
) { | ||
@@ -174,3 +246,4 @@ ret = {$: ret, $types: {$: types}}; | ||
} | ||
} else if (isObject(ret) && ret.hasOwnProperty('$types')) { // No special types | ||
// No special types | ||
} else if (isObject(ret) && hasOwn.call(ret, '$types')) { | ||
ret = {$: ret, $types: true}; | ||
@@ -183,47 +256,66 @@ } | ||
} | ||
function checkPromises (ret, promisesData) { | ||
return Promise.all( | ||
/** | ||
* | ||
* @param {*} ret | ||
* @param {array} promisesData | ||
* @returns {Promise} Resolves to ... | ||
*/ | ||
async function checkPromises (ret, promisesData) { | ||
const promResults = await Promise.all( | ||
promisesData.map((pd) => { return pd[1].p; }) | ||
).then(function (promResults) { | ||
return Promise.all( | ||
promResults.map(function (promResult) { | ||
const newPromisesData = []; | ||
const prData = promisesData.splice(0, 1)[0]; | ||
const [keyPath, , cyclic, stateObj, parentObj, key, detectedType] = prData; | ||
); | ||
await Promise.all( | ||
promResults.map(async function (promResult) { | ||
const newPromisesData = []; | ||
const [prData] = promisesData.splice(0, 1); | ||
const [ | ||
keyPath, , cyclic, stateObj, | ||
parentObj, key, detectedType | ||
] = prData; | ||
const encaps = _encapsulate(keyPath, promResult, cyclic, stateObj, newPromisesData, true, detectedType); | ||
const isTypesonPromise = hasConstructorOf(encaps, TypesonPromise); | ||
if (keyPath && isTypesonPromise) { // Handle case where an embedded custom type itself returns a `Typeson.Promise` | ||
return encaps.p.then(function (encaps2) { | ||
parentObj[key] = encaps2; | ||
return checkPromises(ret, newPromisesData); | ||
}); | ||
} | ||
if (keyPath) parentObj[key] = encaps; | ||
else if (isTypesonPromise) { | ||
ret = encaps.p; | ||
} else ret = encaps; // If this is itself a `Typeson.Promise` (because the original value supplied was a promise or because the supplied custom type value resolved to one), returning it below will be fine since a promise is expected anyways given current config (and if not a promise, it will be ready as the resolve value) | ||
const encaps = _encapsulate( | ||
keyPath, promResult, cyclic, stateObj, | ||
newPromisesData, true, detectedType | ||
); | ||
const isTypesonPromise = hasConstructorOf( | ||
encaps, | ||
TypesonPromise | ||
); | ||
// Handle case where an embedded custom type itself | ||
// returns a `Typeson.Promise` | ||
if (keyPath && isTypesonPromise) { | ||
const encaps2 = await encaps.p; | ||
parentObj[key] = encaps2; | ||
return checkPromises(ret, newPromisesData); | ||
}) | ||
); | ||
}).then(() => ret); | ||
}; | ||
return promisesDataRoot.length | ||
? sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError('Sync method requested but async result obtained'); | ||
})() | ||
: Promise.resolve(checkPromises(ret, promisesDataRoot)).then(finish) | ||
: !sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError('Async method requested but sync result obtained'); | ||
})() | ||
: (opts.stringification && sync // If this is a synchronous request for stringification, yet a promise is the result, we don't want to resolve leading to an async result, so we return an array to avoid ambiguity | ||
? [finish(ret)] | ||
: (sync | ||
? finish(ret) | ||
: Promise.resolve(finish(ret)) | ||
)); | ||
} | ||
if (keyPath) { | ||
parentObj[key] = encaps; | ||
} else if (isTypesonPromise) { | ||
ret = encaps.p; | ||
} else { | ||
// If this is itself a `Typeson.Promise` (because the | ||
// original value supplied was a `Promise` or | ||
// because the supplied custom type value resolved | ||
// to one), returning it below will be fine since | ||
// a `Promise` is expected anyways given current | ||
// config (and if not a `Promise`, it will be ready | ||
// as the resolve value) | ||
ret = encaps; | ||
} | ||
return checkPromises(ret, newPromisesData); | ||
}) | ||
); | ||
return ret; | ||
} | ||
function _adaptBuiltinStateObjectProperties (stateObj, ownKeysObj, cb) { | ||
/** | ||
* | ||
* @param {object} stateObj | ||
* @param {object} ownKeysObj | ||
* @param {function} cb | ||
* @returns {undefined} | ||
*/ | ||
function _adaptBuiltinStateObjectProperties ( | ||
stateObj, ownKeysObj, cb | ||
) { | ||
Object.assign(stateObj, ownKeysObj); | ||
@@ -240,24 +332,51 @@ const vals = internalStateObjPropsToIgnore.map((prop) => { | ||
} | ||
function _encapsulate (keypath, value, cyclic, stateObj, promisesData, resolvingTypesonPromise, detectedType) { | ||
/** | ||
* | ||
* @param {string} keypath | ||
* @param {*} value | ||
* @param {boolean} cyclic | ||
* @param {object} stateObj | ||
* @param {boolean} promisesData | ||
* @param {boolean} resolvingTypesonPromise | ||
* @param {string} detectedType | ||
* @returns {*} | ||
*/ | ||
function _encapsulate ( | ||
keypath, value, cyclic, stateObj, promisesData, | ||
resolvingTypesonPromise, detectedType | ||
) { | ||
let ret; | ||
let observerData = {}; | ||
const $typeof = typeof value; | ||
const runObserver = encapsulateObserver ? function (obj) { | ||
const type = detectedType || stateObj.type || ( | ||
Typeson.getJSONType(value) | ||
); | ||
encapsulateObserver(Object.assign(obj || observerData, { | ||
keypath, | ||
value, | ||
cyclic, | ||
stateObj, | ||
promisesData, | ||
resolvingTypesonPromise, | ||
awaitingTypesonPromise: hasConstructorOf(value, TypesonPromise) | ||
}, type !== undefined ? {type} : {})); | ||
} : null; | ||
if ($typeof in {string: 1, boolean: 1, number: 1, undefined: 1}) { | ||
const runObserver = encapsulateObserver | ||
? function (obj) { | ||
const type = detectedType || stateObj.type || ( | ||
Typeson.getJSONType(value) | ||
); | ||
encapsulateObserver(Object.assign(obj || observerData, { | ||
keypath, | ||
value, | ||
cyclic, | ||
stateObj, | ||
promisesData, | ||
resolvingTypesonPromise, | ||
awaitingTypesonPromise: hasConstructorOf( | ||
value, | ||
TypesonPromise | ||
) | ||
}, type !== undefined ? {type} : {})); | ||
} | ||
: null; | ||
if (['string', 'boolean', 'number', 'undefined'].includes( | ||
$typeof | ||
)) { | ||
if (value === undefined || ($typeof === 'number' && | ||
(isNaN(value) || value === -Infinity || value === Infinity))) { | ||
ret = replace(keypath, value, stateObj, promisesData, false, resolvingTypesonPromise, runObserver); | ||
(isNaN(value) || value === -Infinity || | ||
value === Infinity) | ||
)) { | ||
ret = replace( | ||
keypath, value, stateObj, promisesData, | ||
false, resolvingTypesonPromise, runObserver | ||
); | ||
if (ret !== value) { | ||
@@ -269,11 +388,18 @@ observerData = {replaced: ret}; | ||
} | ||
if (runObserver) runObserver(); | ||
if (runObserver) { | ||
runObserver(); | ||
} | ||
return ret; | ||
} | ||
if (value === null) { | ||
if (runObserver) runObserver(); | ||
if (runObserver) { | ||
runObserver(); | ||
} | ||
return value; | ||
} | ||
if (cyclic && !stateObj.iterateIn && !stateObj.iterateUnsetNumeric) { | ||
// Options set to detect cyclic references and be able to rewrite them. | ||
if (cyclic && !stateObj.iterateIn && | ||
!stateObj.iterateUnsetNumeric | ||
) { | ||
// Options set to detect cyclic references and be able | ||
// to rewrite them. | ||
const refIndex = refObjs.indexOf(value); | ||
@@ -298,8 +424,18 @@ if (refIndex < 0) { | ||
const replaced = ( | ||
((isPlainObj || isArr) && (!plainObjectReplacers.length || stateObj.replaced)) || | ||
stateObj.iterateIn // Running replace will cause infinite loop as will test positive again | ||
// Running replace will cause infinite loop as will test | ||
// positive again | ||
((isPlainObj || isArr) && | ||
(!that.plainObjectReplacers.length || | ||
stateObj.replaced)) || | ||
stateObj.iterateIn | ||
) | ||
// Optimization: if plain object and no plain-object replacers, don't try finding a replacer | ||
// Optimization: if plain object and no plain-object | ||
// replacers, don't try finding a replacer | ||
? value | ||
: replace(keypath, value, stateObj, promisesData, isPlainObj || isArr, null, runObserver); | ||
: replace( | ||
keypath, value, stateObj, promisesData, | ||
isPlainObj || isArr, | ||
null, | ||
runObserver | ||
); | ||
let clone; | ||
@@ -310,3 +446,5 @@ if (replaced !== value) { | ||
} else { | ||
if (isArr || stateObj.iterateIn === 'array') { | ||
if ((isArr && stateObj.iterateIn !== 'object') || | ||
stateObj.iterateIn === 'array' | ||
) { | ||
clone = new Array(value.length); | ||
@@ -316,5 +454,13 @@ observerData = {clone}; | ||
clone = {}; | ||
if (stateObj.addLength) { | ||
clone.length = value.length; | ||
} | ||
observerData = {clone}; | ||
} else if (keypath === '' && hasConstructorOf(value, TypesonPromise)) { | ||
promisesData.push([keypath, value, cyclic, stateObj, undefined, undefined, stateObj.type]); | ||
} else if (keypath === '' && | ||
hasConstructorOf(value, TypesonPromise) | ||
) { | ||
promisesData.push([ | ||
keypath, value, cyclic, stateObj, | ||
undefined, undefined, stateObj.type | ||
]); | ||
ret = value; | ||
@@ -325,3 +471,5 @@ } else { | ||
} | ||
if (runObserver) runObserver(); | ||
if (runObserver) { | ||
runObserver(); | ||
} | ||
@@ -339,26 +487,62 @@ if (opts.iterateNone) { | ||
for (const key in value) { | ||
const ownKeysObj = {ownKeys: value.hasOwnProperty(key)}; | ||
_adaptBuiltinStateObjectProperties(stateObj, ownKeysObj, () => { | ||
const kp = keypath + (keypath ? '.' : '') + escapeKeyPathComponent(key); | ||
const val = _encapsulate(kp, value[key], !!cyclic, stateObj, promisesData, resolvingTypesonPromise); | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
promisesData.push([kp, val, !!cyclic, stateObj, clone, key, stateObj.type]); | ||
} else if (val !== undefined) clone[key] = val; | ||
}); | ||
const ownKeysObj = {ownKeys: hasOwn.call(value, key)}; | ||
_adaptBuiltinStateObjectProperties( | ||
stateObj, | ||
ownKeysObj, | ||
() => { | ||
const kp = keypath + (keypath ? '.' : '') + | ||
escapeKeyPathComponent(key); | ||
const val = _encapsulate( | ||
kp, value[key], !!cyclic, stateObj, | ||
promisesData, resolvingTypesonPromise | ||
); | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
promisesData.push([ | ||
kp, val, !!cyclic, stateObj, | ||
clone, key, stateObj.type | ||
]); | ||
} else if (val !== undefined) { | ||
clone[key] = val; | ||
} | ||
} | ||
); | ||
} | ||
if (runObserver) runObserver({endIterateIn: true, end: true}); | ||
} else { // Note: Non-indexes on arrays won't survive stringify so somewhat wasteful for arrays, but so too is iterating all numeric indexes on sparse arrays when not wanted or filtering own keys for positive integers | ||
if (runObserver) { | ||
runObserver({endIterateIn: true, end: true}); | ||
} | ||
} else { | ||
// Note: Non-indexes on arrays won't survive stringify so | ||
// somewhat wasteful for arrays, but so too is iterating | ||
// all numeric indexes on sparse arrays when not wanted | ||
// or filtering own keys for positive integers | ||
keys(value).forEach(function (key) { | ||
const kp = keypath + (keypath ? '.' : '') + escapeKeyPathComponent(key); | ||
const kp = keypath + (keypath ? '.' : '') + | ||
escapeKeyPathComponent(key); | ||
const ownKeysObj = {ownKeys: true}; | ||
_adaptBuiltinStateObjectProperties(stateObj, ownKeysObj, () => { | ||
const val = _encapsulate(kp, value[key], !!cyclic, stateObj, promisesData, resolvingTypesonPromise); | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
promisesData.push([kp, val, !!cyclic, stateObj, clone, key, stateObj.type]); | ||
} else if (val !== undefined) clone[key] = val; | ||
}); | ||
_adaptBuiltinStateObjectProperties( | ||
stateObj, | ||
ownKeysObj, | ||
() => { | ||
const val = _encapsulate( | ||
kp, value[key], !!cyclic, stateObj, | ||
promisesData, resolvingTypesonPromise | ||
); | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
promisesData.push([ | ||
kp, val, !!cyclic, stateObj, | ||
clone, key, stateObj.type | ||
]); | ||
} else if (val !== undefined) { | ||
clone[key] = val; | ||
} | ||
} | ||
); | ||
}); | ||
if (runObserver) runObserver({endIterateOwn: true, end: true}); | ||
if (runObserver) { | ||
runObserver({endIterateOwn: true, end: true}); | ||
} | ||
} | ||
// Iterate array for non-own numeric properties (we can't replace the prior loop though as it iterates non-integer keys) | ||
// Iterate array for non-own numeric properties (we can't | ||
// replace the prior loop though as it iterates non-integer | ||
// keys) | ||
if (stateObj.iterateUnsetNumeric) { | ||
@@ -368,13 +552,29 @@ const vl = value.length; | ||
if (!(i in value)) { | ||
const kp = keypath + (keypath ? '.' : '') + i; // No need to escape numeric | ||
// No need to escape numeric | ||
const kp = keypath + (keypath ? '.' : '') + i; | ||
const ownKeysObj = {ownKeys: false}; | ||
_adaptBuiltinStateObjectProperties(stateObj, ownKeysObj, () => { | ||
const val = _encapsulate(kp, undefined, !!cyclic, stateObj, promisesData, resolvingTypesonPromise); | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
promisesData.push([kp, val, !!cyclic, stateObj, clone, i, stateObj.type]); | ||
} else if (val !== undefined) clone[i] = val; | ||
}); | ||
_adaptBuiltinStateObjectProperties( | ||
stateObj, | ||
ownKeysObj, | ||
() => { | ||
const val = _encapsulate( | ||
kp, undefined, !!cyclic, stateObj, | ||
promisesData, resolvingTypesonPromise | ||
); | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
promisesData.push([ | ||
kp, val, !!cyclic, stateObj, | ||
clone, i, stateObj.type | ||
]); | ||
} else if (val !== undefined) { | ||
clone[i] = val; | ||
} | ||
} | ||
); | ||
} | ||
} | ||
if (runObserver) runObserver({endIterateUnsetNumeric: true, end: true}); | ||
if (runObserver) { | ||
runObserver({endIterateUnsetNumeric: true, end: true}); | ||
} | ||
} | ||
@@ -384,5 +584,21 @@ return clone; | ||
function replace (keypath, value, stateObj, promisesData, plainObject, resolvingTypesonPromise, runObserver) { | ||
/** | ||
* | ||
* @param {string} keypath | ||
* @param {*} value | ||
* @param {object} stateObj | ||
* @param {array} promisesData | ||
* @param {boolean} plainObject | ||
* @param {boolean} resolvingTypesonPromise | ||
* @param {function} [runObserver] | ||
* @returns {*} | ||
*/ | ||
function replace ( | ||
keypath, value, stateObj, promisesData, plainObject, | ||
resolvingTypesonPromise, runObserver | ||
) { | ||
// Encapsulate registered types | ||
const replacers = plainObject ? plainObjectReplacers : nonplainObjectReplacers; | ||
const replacers = plainObject | ||
? that.plainObjectReplacers | ||
: that.nonplainObjectReplacers; | ||
let i = replacers.length; | ||
@@ -393,21 +609,42 @@ while (i--) { | ||
const {type} = replacer; | ||
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. | ||
if (that.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. | ||
const existing = types[keypath]; | ||
// type can comprise an array of types (see test shouldSupportIntermediateTypes) | ||
types[keypath] = existing ? [type].concat(existing) : type; | ||
// type can comprise an array of types (see test | ||
// `shouldSupportIntermediateTypes`) | ||
types[keypath] = existing | ||
? [type].concat(existing) | ||
: type; | ||
} | ||
// Now, also traverse the result in case it contains its own types to replace | ||
// Now, also traverse the result in case it contains its | ||
// own types to replace | ||
Object.assign(stateObj, {type, replaced: true}); | ||
if ((sync || !replacer.replaceAsync) && !replacer.replace) { | ||
if (runObserver) runObserver({typeDetected: true}); | ||
return _encapsulate(keypath, value, cyclic && 'readonly', stateObj, promisesData, resolvingTypesonPromise, type); | ||
if ((sync || !replacer.replaceAsync) && | ||
!replacer.replace | ||
) { | ||
if (runObserver) { | ||
runObserver({typeDetected: true}); | ||
} | ||
return _encapsulate( | ||
keypath, value, cyclic && 'readonly', stateObj, | ||
promisesData, resolvingTypesonPromise, type | ||
); | ||
} | ||
if (runObserver) runObserver({replacing: true}); | ||
if (runObserver) { | ||
runObserver({replacing: true}); | ||
} | ||
const replaceMethod = sync || !replacer.replaceAsync ? 'replace' : 'replaceAsync'; | ||
return _encapsulate(keypath, replacer[replaceMethod](value, stateObj), cyclic && 'readonly', stateObj, promisesData, resolvingTypesonPromise, type); | ||
const replaceMethod = sync || !replacer.replaceAsync | ||
? 'replace' | ||
: 'replaceAsync'; | ||
return _encapsulate( | ||
keypath, replacer[replaceMethod](value, stateObj), | ||
cyclic && 'readonly', stateObj, promisesData, | ||
resolvingTypesonPromise, type | ||
); | ||
} | ||
@@ -417,26 +654,90 @@ } | ||
} | ||
}; | ||
// Also sync but throws on non-sync result | ||
this.encapsulateSync = function (obj, stateObj, opts) { | ||
return encapsulate(obj, stateObj, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: true})); | ||
}; | ||
this.encapsulateAsync = function (obj, stateObj, opts) { | ||
return encapsulate(obj, stateObj, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: false})); | ||
}; | ||
return promisesDataRoot.length | ||
? sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError( | ||
'Sync method requested but async result obtained' | ||
); | ||
})() | ||
: Promise.resolve( | ||
checkPromises(ret, promisesDataRoot) | ||
).then(finish) | ||
: !sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError( | ||
'Async method requested but sync result obtained' | ||
); | ||
})() | ||
// If this is a synchronous request for stringification, yet | ||
// a promise is the result, we don't want to resolve leading | ||
// to an async result, so we return an array to avoid | ||
// ambiguity | ||
: (opts.stringification && sync | ||
? [finish(ret)] | ||
: (sync | ||
? finish(ret) | ||
: Promise.resolve(finish(ret)) | ||
)); | ||
} | ||
/** Revive an encapsulated object. | ||
* This method is used internally by Typeson.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. | ||
/** | ||
* Also sync but throws on non-sync result | ||
* @param {*} obj | ||
* @param {object} stateObj | ||
* @param {object} opts | ||
* @returns {*} | ||
*/ | ||
const revive = this.revive = function (obj, opts) { | ||
opts = Object.assign({sync: true}, options, opts); | ||
encapsulateSync (obj, stateObj, opts) { | ||
return this.encapsulate(obj, stateObj, { | ||
throwOnBadSyncType: true, ...opts, sync: true | ||
}); | ||
} | ||
/** | ||
* @param {*} obj | ||
* @param {object} stateObj | ||
* @param {object} opts | ||
* @returns {*} | ||
*/ | ||
encapsulateAsync (obj, stateObj, opts) { | ||
return this.encapsulate(obj, stateObj, { | ||
throwOnBadSyncType: true, ...opts, sync: false | ||
}); | ||
} | ||
/** | ||
* Revive an encapsulated object. | ||
* This method is used internally by `Typeson.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. | ||
* @param {object} opts | ||
* @throws TypeError If mismatch between sync/async type and result | ||
* @returns {Promise|*} If async, returns a Promise that resolves to `*` | ||
*/ | ||
revive (obj, opts) { | ||
let types = obj && obj.$types; | ||
// No type info added. Revival not needed. | ||
if (!types) { | ||
return obj; | ||
} | ||
// Object happened to have own `$types` property but with | ||
// no actual types, so we unescape and return that object | ||
if (types === true) { | ||
return obj.$; | ||
} | ||
opts = {sync: true, ...this.options, ...opts}; | ||
const {sync} = opts; | ||
let types = obj && obj.$types, | ||
ignore$Types = true; | ||
if (!types) return obj; // No type info added. Revival not needed. | ||
if (types === true) return obj.$; // Object happened to have own `$types` property but with no actual types, so we unescape and return that object | ||
const keyPathResolutions = []; | ||
const stateObj = {}; | ||
let ignore$Types = true; | ||
// Special when root object is not a trivial Object, it will | ||
// be encapsulated in `$`. It will also be encapsulated in | ||
// `$` if it has its own `$` property to avoid ambiguity | ||
if (types.$ && isPlainObject(types.$)) { | ||
// Special when root object is not a trivial Object, it will be encapsulated in $. It will also be encapsulated in $ if it has its own `$` property to avoid ambiguity | ||
obj = obj.$; | ||
@@ -446,22 +747,98 @@ types = types.$; | ||
} | ||
const keyPathResolutions = []; | ||
const stateObj = {}; | ||
let ret = _revive('', obj, null, opts); | ||
ret = hasConstructorOf(ret, Undefined) ? undefined : ret; | ||
return isThenable(ret) | ||
? sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError('Sync method requested but async result obtained'); | ||
})() | ||
: ret | ||
: !sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError('Async method requested but sync result obtained'); | ||
})() | ||
: sync | ||
? ret | ||
: Promise.resolve(ret); | ||
function _revive (keypath, value, target, opts, clone, key) { | ||
if (ignore$Types && keypath === '$types') return; | ||
const that = this; | ||
function revivePlainObjects () { | ||
// const references = []; | ||
// const reviveTypes = []; | ||
const plainObjectTypes = []; | ||
Object.entries(types).forEach(([ | ||
keypath, type | ||
]) => { | ||
if (type === '#') { | ||
/* | ||
references.push({ | ||
keypath, | ||
reference: getByKeyPath(obj, keypath) | ||
}); | ||
*/ | ||
return; | ||
} | ||
[].concat(type).forEach(function (type) { | ||
const [, {plain}] = that.revivers[type]; | ||
if (!plain) { | ||
// reviveTypes.push({keypath, type}); | ||
return; | ||
} | ||
plainObjectTypes.push({keypath, type}); | ||
delete types[keypath]; // Avoid repeating | ||
}); | ||
}); | ||
if (!plainObjectTypes.length) { | ||
return; | ||
} | ||
// Handle plain object revivers first so reference | ||
// setting can use revived type (e.g., array instead | ||
// of object); assumes revived has same structure | ||
// or will otherwise break subsequent references | ||
return plainObjectTypes.sort(nestedPathsFirst).reduce( | ||
function reducer (possibleTypesonPromise, { | ||
keypath, type | ||
}) { | ||
if (hasConstructorOf( | ||
possibleTypesonPromise, TypesonPromise | ||
)) { | ||
// TypesonPromise here too | ||
return possibleTypesonPromise.then((v) => { | ||
return reducer(v, type); | ||
}); | ||
} | ||
let val = getByKeyPath(obj, keypath); | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
return val.then((v) => { // TypesonPromise here too | ||
return reducer(v, type); | ||
}); | ||
} | ||
const [reviver] = that.revivers[type]; | ||
if (!reviver) { | ||
throw new Error('Unregistered type: ' + type); | ||
} | ||
val = reviver[ | ||
sync && reviver.revive | ||
? 'revive' | ||
: !sync && reviver.reviveAsync | ||
? 'reviveAsync' | ||
: 'revive' | ||
](val, stateObj); | ||
if (val === undefined) { | ||
return undefined; | ||
} | ||
if (hasConstructorOf(val, Undefined)) { | ||
val = undefined; | ||
} | ||
const newVal = setAtKeyPath(obj, keypath, val); | ||
if (newVal === val) { | ||
obj = val; | ||
} | ||
return undefined; | ||
}, | ||
undefined // This argument must be explicit | ||
); | ||
// references.forEach(({keypath, reference}) => {}); | ||
// reviveTypes.sort(nestedPathsFirst).forEach(() => {}); | ||
} | ||
/** | ||
* | ||
* @param {string} keypath | ||
* @param {*} value | ||
* @param {?(Array|object)} target | ||
* @param {Array|object} [clone] | ||
* @param {string} [key] | ||
* @returns {*} | ||
*/ | ||
function _revive (keypath, value, target, clone, key) { | ||
if (ignore$Types && keypath === '$types') { | ||
return undefined; | ||
} | ||
const type = types[keypath]; | ||
@@ -471,36 +848,54 @@ if (isArray(value) || isPlainObject(value)) { | ||
// Iterate object or array | ||
keys(value).forEach((key) => { | ||
keys(value).forEach((k) => { | ||
const val = _revive( | ||
keypath + (keypath ? '.' : '') + escapeKeyPathComponent(key), value[key], | ||
keypath + (keypath ? '.' : '') + | ||
escapeKeyPathComponent(k), value[k], | ||
target || clone, | ||
opts, | ||
clone, | ||
key | ||
k | ||
); | ||
if (hasConstructorOf(val, Undefined)) clone[key] = undefined; | ||
else if (val !== undefined) clone[key] = val; | ||
if (hasConstructorOf(val, Undefined)) { | ||
clone[k] = undefined; | ||
} else if (val !== undefined) { | ||
clone[k] = val; | ||
} | ||
}); | ||
value = clone; | ||
while (keyPathResolutions.length) { // Try to resolve cyclic reference as soon as available | ||
const [[target, keyPath, clone, key]] = keyPathResolutions; | ||
// Try to resolve cyclic reference as soon as available | ||
while (keyPathResolutions.length) { | ||
const [[target, keyPath, clone, k]] = keyPathResolutions; | ||
const val = getByKeyPath(target, keyPath); | ||
if (hasConstructorOf(val, Undefined)) clone[key] = undefined; | ||
else if (val !== undefined) clone[key] = val; | ||
else break; | ||
if (hasConstructorOf(val, Undefined)) { | ||
clone[k] = undefined; | ||
} else if (val !== undefined) { | ||
clone[k] = val; | ||
} else { | ||
break; | ||
} | ||
keyPathResolutions.splice(0, 1); | ||
} | ||
} | ||
if (!type) return value; | ||
if (!type) { | ||
return value; | ||
} | ||
if (type === '#') { | ||
const ret = getByKeyPath(target, value.substr(1)); | ||
const ret = getByKeyPath(target, value.slice(1)); | ||
if (ret === undefined) { // Cyclic reference not yet available | ||
keyPathResolutions.push([target, value.substr(1), clone, key]); | ||
keyPathResolutions.push([ | ||
target, value.slice(1), clone, key | ||
]); | ||
} | ||
return ret; | ||
} | ||
const {sync} = opts; | ||
return [].concat(type).reduce((val, type) => { | ||
const reviver = revivers[type]; | ||
if (!reviver) throw new Error('Unregistered type: ' + type); | ||
return reviver[ // eslint-disable-line standard/computed-property-even-spacing | ||
return [].concat(type).reduce(function reducer (val, type) { | ||
if (hasConstructorOf(val, TypesonPromise)) { | ||
return val.then((v) => { // TypesonPromise here too | ||
return reducer(v, type); | ||
}); | ||
} | ||
const [reviver] = that.revivers[type]; | ||
if (!reviver) { | ||
throw new Error('Unregistered type: ' + type); | ||
} | ||
return reviver[ | ||
sync && reviver.revive | ||
@@ -514,156 +909,173 @@ ? 'revive' | ||
} | ||
}; | ||
// Also sync but throws on non-sync result | ||
this.reviveSync = function (obj, opts) { | ||
return revive(obj, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: true})); | ||
}; | ||
this.reviveAsync = function (obj, opts) { | ||
return revive(obj, Object.assign({}, {throwOnBadSyncType: true}, opts, {sync: false})); | ||
}; | ||
function checkUndefined (retrn) { | ||
return hasConstructorOf(retrn, Undefined) ? undefined : retrn; | ||
} | ||
/** 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]; | ||
const possibleTypesonPromise = revivePlainObjects(); | ||
let ret; | ||
if (hasConstructorOf(possibleTypesonPromise, TypesonPromise)) { | ||
ret = possibleTypesonPromise.then(() => { | ||
return _revive('', obj, null); | ||
}); | ||
} else { | ||
ret = _revive('', obj, null); | ||
} | ||
return isThenable(ret) | ||
? sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError( | ||
'Sync method requested but async result obtained' | ||
); | ||
})() | ||
: hasConstructorOf(ret, TypesonPromise) | ||
? ret.p.then(checkUndefined) | ||
: ret | ||
: !sync && opts.throwOnBadSyncType | ||
? (() => { | ||
throw new TypeError( | ||
'Async method requested but sync result obtained' | ||
); | ||
})() | ||
: sync | ||
? checkUndefined(ret) | ||
: Promise.resolve(checkUndefined(ret)); | ||
} | ||
/** | ||
* Also sync but throws on non-sync result | ||
* @param {*} obj | ||
* @param {object} opts | ||
* @returns {*} | ||
*/ | ||
this.register = function (typeSpecSets, opts) { | ||
reviveSync (obj, opts) { | ||
return this.revive(obj, { | ||
throwOnBadSyncType: true, ...opts, sync: true | ||
}); | ||
} | ||
/** | ||
* @param {*} obj | ||
* @param {object} opts | ||
* @returns {Promise} Resolves to `*` | ||
*/ | ||
reviveAsync (obj, opts) { | ||
return this.revive(obj, { | ||
throwOnBadSyncType: true, ...opts, sync: false | ||
}); | ||
} | ||
/** | ||
* Register types. | ||
* For examples on how to use this method, see | ||
* {@link https://github.com/dfahlander/typeson-registry/tree/master/types} | ||
* @param {Array.<Object.<string,Function[]>>} typeSpecSets - Types and | ||
* their functions [test, encapsulate, revive]; | ||
* @param {object} opts | ||
* @returns {Typeson} | ||
*/ | ||
register (typeSpecSets, opts) { | ||
opts = opts || {}; | ||
[].concat(typeSpecSets).forEach(function R (typeSpec) { | ||
if (isArray(typeSpec)) return typeSpec.map(R); // Allow arrays of arrays of arrays... | ||
// Allow arrays of arrays of arrays... | ||
if (isArray(typeSpec)) { | ||
return typeSpec.map(R, this); | ||
} | ||
typeSpec && keys(typeSpec).forEach(function (typeId) { | ||
if (typeId === '#') { | ||
throw new TypeError('# cannot be used as a type name as it is reserved for cyclic objects'); | ||
throw new TypeError( | ||
'# cannot be used as a type name as it is reserved ' + | ||
'for cyclic objects' | ||
); | ||
} else if (Typeson.JSON_TYPES.includes(typeId)) { | ||
throw new TypeError('Plain JSON object types are reserved as type names'); | ||
throw new TypeError( | ||
'Plain JSON object types are reserved as type names' | ||
); | ||
} | ||
let spec = typeSpec[typeId]; | ||
const replacers = spec.testPlainObjects ? plainObjectReplacers : nonplainObjectReplacers; | ||
const existingReplacer = replacers.filter(function (r) { return r.type === typeId; }); | ||
const replacers = spec.testPlainObjects | ||
? this.plainObjectReplacers | ||
: this.nonplainObjectReplacers; | ||
const existingReplacer = replacers.filter(function (r) { | ||
return r.type === typeId; | ||
}); | ||
if (existingReplacer.length) { | ||
// Remove existing spec and replace with this one. | ||
replacers.splice(replacers.indexOf(existingReplacer[0]), 1); | ||
delete revivers[typeId]; | ||
delete regTypes[typeId]; | ||
delete this.revivers[typeId]; | ||
delete this.types[typeId]; | ||
} | ||
if (spec) { | ||
if (typeof spec === 'function') { | ||
// Support registering just a class without replacer/reviver | ||
const Class = spec; | ||
spec = { | ||
test: (x) => x && x.constructor === Class, | ||
replace: (x) => assign({}, x), | ||
revive: (x) => assign(Object.create(Class.prototype), x) | ||
}; | ||
} else if (isArray(spec)) { | ||
spec = { | ||
test: spec[0], | ||
replace: spec[1], | ||
revive: spec[2] | ||
}; | ||
} | ||
const replacerObj = { | ||
type: typeId, | ||
test: spec.test.bind(spec) | ||
if (!spec) { | ||
return; | ||
} | ||
if (typeof spec === 'function') { | ||
// Support registering just a class without replacer/reviver | ||
const Class = spec; | ||
spec = { | ||
test: (x) => x && x.constructor === Class, | ||
replace: (x) => Object.assign({}, x), | ||
revive: (x) => Object.assign( | ||
Object.create(Class.prototype), x | ||
) | ||
}; | ||
if (spec.replace) { | ||
replacerObj.replace = spec.replace.bind(spec); | ||
} else if (isArray(spec)) { | ||
const [test, replace, revive] = spec; | ||
spec = {test, replace, revive}; | ||
} | ||
const replacerObj = { | ||
type: typeId, | ||
test: spec.test.bind(spec) | ||
}; | ||
if (spec.replace) { | ||
replacerObj.replace = spec.replace.bind(spec); | ||
} | ||
if (spec.replaceAsync) { | ||
replacerObj.replaceAsync = spec.replaceAsync.bind(spec); | ||
} | ||
const start = typeof opts.fallback === 'number' | ||
? opts.fallback | ||
: (opts.fallback ? 0 : Infinity); | ||
if (spec.testPlainObjects) { | ||
this.plainObjectReplacers.splice(start, 0, replacerObj); | ||
} else { | ||
this.nonplainObjectReplacers.splice(start, 0, replacerObj); | ||
} | ||
// Todo: We might consider a testAsync type | ||
if (spec.revive || spec.reviveAsync) { | ||
const reviverObj = {}; | ||
if (spec.revive) { | ||
reviverObj.revive = spec.revive.bind(spec); | ||
} | ||
if (spec.replaceAsync) { | ||
replacerObj.replaceAsync = spec.replaceAsync.bind(spec); | ||
if (spec.reviveAsync) { | ||
reviverObj.reviveAsync = spec.reviveAsync.bind(spec); | ||
} | ||
const start = typeof opts.fallback === 'number' ? opts.fallback : (opts.fallback ? 0 : Infinity); | ||
if (spec.testPlainObjects) { | ||
plainObjectReplacers.splice(start, 0, replacerObj); | ||
} else { | ||
nonplainObjectReplacers.splice(start, 0, replacerObj); | ||
} | ||
// Todo: We might consider a testAsync type | ||
if (spec.revive || spec.reviveAsync) { | ||
const reviverObj = {}; | ||
if (spec.revive) reviverObj.revive = spec.revive.bind(spec); | ||
if (spec.reviveAsync) reviverObj.reviveAsync = spec.reviveAsync.bind(spec); | ||
revivers[typeId] = reviverObj; | ||
} | ||
this.revivers[typeId] = [reviverObj, { | ||
plain: spec.testPlainObjects | ||
}]; | ||
} | ||
regTypes[typeId] = spec; // Record to be retrieved via public types property. | ||
} | ||
}); | ||
}); | ||
// Record to be retrieved via public types property. | ||
this.types[typeId] = spec; | ||
}, this); | ||
}, this); | ||
return this; | ||
}; | ||
} | ||
function assign (t, s) { | ||
keys(s).map((k) => { t[k] = s[k]; }); | ||
return t; | ||
} | ||
/** escapeKeyPathComponent() utility */ | ||
function escapeKeyPathComponent (keyPathComponent) { | ||
return keyPathComponent.replace(/~/g, '~0').replace(/\./g, '~1'); | ||
} | ||
/** unescapeKeyPathComponent() utility */ | ||
function unescapeKeyPathComponent (keyPathComponent) { | ||
return keyPathComponent.replace(/~1/g, '.').replace(/~0/g, '~'); | ||
} | ||
/** getByKeyPath() utility */ | ||
function getByKeyPath (obj, keyPath) { | ||
if (keyPath === '') return obj; | ||
const period = keyPath.indexOf('.'); | ||
if (period > -1) { | ||
const innerObj = obj[unescapeKeyPathComponent(keyPath.substr(0, period))]; | ||
return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1)); | ||
} | ||
return obj[unescapeKeyPathComponent(keyPath)]; | ||
} | ||
// We keep these two functions minimized so if using two instances of this | ||
// library, where one is minimized and one is not, it will still work | ||
function Undefined(){} // eslint-disable-line space-before-function-paren, space-before-blocks | ||
// With ES6 classes, we may be able to simply use `class TypesonPromise extends Promise` and add a string tag for detection | ||
function TypesonPromise(f){this.p=new Promise(f)} // eslint-disable-line block-spacing, space-before-function-paren, space-before-blocks, space-infix-ops, semi | ||
/** | ||
* We keep this function minimized so if using two instances of this | ||
* library, where one is minimized and one is not, it will still work | ||
* with `hasConstructorOf`. | ||
* @constructor | ||
*/ | ||
class Undefined{} // eslint-disable-line space-before-blocks | ||
if (typeof Symbol !== 'undefined') { // Note: @babel/polyfill provides | ||
// Ensure `isUserObject` will return `false` for `TypesonPromise` | ||
TypesonPromise.prototype[Symbol.toStringTag] = 'TypesonPromise'; | ||
} | ||
// The following provide classes meant to avoid clashes with other values | ||
TypesonPromise.prototype.then = function (onFulfilled, onRejected) { | ||
return new TypesonPromise((typesonResolve, typesonReject) => { | ||
this.p.then(function (res) { | ||
typesonResolve(onFulfilled ? onFulfilled(res) : res); | ||
}, (r) => { | ||
this.p['catch'](function (res) { | ||
return onRejected ? onRejected(res) : Promise.reject(res); | ||
}).then(typesonResolve, typesonReject); | ||
}); | ||
}); | ||
}; | ||
TypesonPromise.prototype['catch'] = function (onRejected) { | ||
return this.then(null, onRejected); | ||
}; | ||
TypesonPromise.resolve = function (v) { | ||
return new TypesonPromise((typesonResolve) => { | ||
typesonResolve(v); | ||
}); | ||
}; | ||
TypesonPromise.reject = function (v) { | ||
return new TypesonPromise((typesonResolve, typesonReject) => { | ||
typesonReject(v); | ||
}); | ||
}; | ||
['all', 'race'].map(function (meth) { | ||
TypesonPromise[meth] = function (promArr) { | ||
return new TypesonPromise(function (typesonResolve, typesonReject) { | ||
Promise[meth](promArr.map((prom) => { return prom.p; })).then(typesonResolve, typesonReject); | ||
}); | ||
}; | ||
}); | ||
// To insist `undefined` should be added | ||
Typeson.Undefined = Undefined; | ||
// To support async encapsulation/stringification | ||
Typeson.Promise = TypesonPromise; | ||
// The following provide classes meant to avoid clashes with other values | ||
Typeson.Undefined = Undefined; // To insist `undefined` should be added | ||
Typeson.Promise = TypesonPromise; // To support async encapsulation/stringification | ||
// Some fundamental type-checking utilities | ||
@@ -680,7 +1092,3 @@ Typeson.isThenable = isThenable; | ||
Typeson.getByKeyPath = getByKeyPath; | ||
Typeson.getJSONType = (value) => | ||
value === null ? 'null' : ( | ||
isArray(value) | ||
? 'array' | ||
: typeof value); | ||
Typeson.getJSONType = getJSONType; | ||
Typeson.JSON_TYPES = [ | ||
@@ -687,0 +1095,0 @@ 'null', 'boolean', 'number', 'string', 'array', 'object' |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
122669
9
1377
1