├── .babelrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── dist ├── u.js └── u.min.js ├── lib ├── array.js ├── boolean.js ├── coder.js ├── core.js ├── fixedchar.js ├── integer.js ├── object.js ├── oneOf.js ├── tuple.js ├── u.js └── varchar.js ├── package.json ├── src ├── array.js ├── boolean.js ├── coder.js ├── core.js ├── fixedchar.js ├── integer.js ├── object.js ├── oneOf.js ├── tuple.js ├── u.js └── varchar.js └── test └── u.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["lodash"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | - "4.0" 5 | - "0.12" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Anantha Kumaran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # μ [![Build Status](https://travis-ci.org/ananthakumaran/u.svg?branch=master)](https://travis-ci.org/ananthakumaran/u) 2 | 3 | Without μ: 4 | `http://app.com/url#%7B%22lookingFor%22:%22bride%22,%22age%22:%5B25,30%5D,%22religion%22:%22Hindu%22,%22motherTongue%22:%22Bengali%22,%22onlyProfileWithPhoto%22:true%7D` 5 | 6 | With μ: 7 | `http://app.com/url#bHhc9I-aqa` 8 | 9 | μ is a JavaScript library for encoding/decoding state (JavaScript 10 | object) in URL. Define a spec for the state, based on which the 11 | encoding is done. Manage the state with versioning. 12 | 13 | ## Example 14 | 15 | Import the library 16 | 17 | `import {fromJson, encode, decode} from "u";` 18 | 19 | Define the spec. 20 | 21 | ```javascript 22 | var spec = { 23 | lookingFor: ['oneOf', 'bride', 'groom'], 24 | age: ['tuple', ['integer'] /* min */, ['integer'] /* max */], 25 | religion: ['oneOf', 'Hindu', 'Muslim', 'Christian', 'Sikh', 'Parsi', 'Jain', 'Buddhist', 'Jewish', 'No Religion', 'Spiritual', 'Other'], 26 | motherTongue: ['oneOf', 'Assamese', 'Bengali', 'English', 'Gujarati', 'Hindi', 'Kannada', 'Konkani', 'Malayalam', 'Marathi', 'Marwari', 'Odia', 'Punjabi', 'Sindhi', 'Tamil', 'Telugu', 'Urdu'], 27 | onlyProfileWithPhoto: ['boolean'] 28 | }; 29 | 30 | var v1 = fromJson(1, spec); 31 | ``` 32 | 33 | Encode the object/state. 34 | 35 | ```javascript 36 | var encodedv1 = encode(v1, {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true}); 37 | //=> 'bHhc9I-aqa' 38 | decode([v1], encodedv1) //=> {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true}); 39 | ``` 40 | 41 | Update your spec, as your application state space grows. Use versioning to 42 | encode/decode state. 43 | 44 | ```javascript 45 | var newSpec = _.extend({}, spec, { 46 | maritialStatus: ['oneOf', "Doesn't Matter", 'Never Married', 'Divorced', 'Widowed', 'Awaiting Divorce', 'Annulled'] 47 | }); 48 | var v2 = fromJson(2, newSpec, function (old) { 49 | old.maritialStatus = "Doesn't Matter"; 50 | return old; 51 | }); 52 | 53 | decode([v1, v2], encodedv1) //=> {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true, maritialStatus: "Doesn't Matter"}); 54 | var encodedv2 = encode(v2, {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true, maritialStatus: 'Never Married'}); 55 | //=> 'cHlc9I-aHaa' 56 | decode([v1, v2], encodedv2) //=> {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true, maritialStatus: 'Never Married'}); 57 | ``` 58 | 59 | ## API 60 | 61 | ### fromJson(version, spec, [migrate]) 62 | 63 | **version** - spec version number 64 | **spec** - used to define the structure and domain of the data. 65 | 66 | *structure* 67 | object is defined using { key: specForValue, ... } 68 | array is defined using ['array', specForValue ] 69 | tuple is defined using ['tuple', specForValueAtIndexZero, specForValueAtIndexOne, ...] 70 | 71 | *domain* 72 | domain is defined using [domainName, arg1, arg2, ...] 73 | 74 | | Domain | Args | Description | 75 | ---------|------|-------------| 76 | | oneOf | allowed values | can be considered similar to enum. As we only encode the index position, the value could be anything | 77 | | integer | | any integer | 78 | | boolean | | true or false | 79 | | fixedchar | Size of the string | fixed length string | 80 | | varchar | | variable length string | 81 | 82 | **migrate** - a function that will get called in case where you decode 83 | an object encoded using older spec. For example, there are three 84 | versions v1, v2, v3 and you try to decode the string encoded using v1, 85 | then the migrate method in v2 and v3 will get called with the decoded 86 | value. 87 | 88 | ### encode(coder, object) 89 | 90 | **coder** - coder created using fromJson 91 | **object** - object that needs to encoded 92 | 93 | ### decode(coders, blob) 94 | 95 | **coders** - array of coder. 96 | **blob** - the string that is returned by encode. 97 | -------------------------------------------------------------------------------- /dist/u.min.js: -------------------------------------------------------------------------------- 1 | !function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.u=r():t.u=r()}(this,function(){return function(t){function r(e){if(n[e])return n[e].exports;var o=n[e]={exports:{},id:e,loaded:!1};return t[e].call(o.exports,o,o.exports,r),o.loaded=!0,o.exports}var n={};return r.m=t,r.c=n,r.p="",r(0)}([function(t,r,n){"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r.register=r.decode=r.encode=r.fromJson=void 0;var e=n(2);Object.defineProperty(r,"fromJson",{enumerable:!0,get:function(){return e.fromJson}}),Object.defineProperty(r,"encode",{enumerable:!0,get:function(){return e.encode}}),Object.defineProperty(r,"decode",{enumerable:!0,get:function(){return e.decode}}),Object.defineProperty(r,"register",{enumerable:!0,get:function(){return e.register}}),n(25),n(51),n(53),n(56),n(52),n(54),n(55),n(50)},function(t,r){var n=Array.isArray;t.exports=n},function(t,r,n){"use strict";function e(t){return t&&t.__esModule?t:{"default":t}}function o(t,r){E[t]=r}function u(t,r){var n=t.spec.encode(r),e=n.bits,o=n.blob;return t.encodedVersion+(0,P.toVarN)(e.length)+(0,P.bitsToN)(e)+o}function i(t,r){var n,e,o=(0,P.fromVarN)(r),u=N(o,2);n=u[0],r=u[1];var i=(0,P.fromVarN)(r),c=N(i,2);e=c[0],r=c[1];var a=(0,M["default"])(t,function(t){return t.version===n});if(!a)throw new Error("Invalid version: "+n);var f=Math.ceil(e/6),s=(0,P.nToBits)(r.substr(0,f),e),l=r.substr(f),v=a.spec.decode({bits:s,blob:l}),p=(0,A["default"])((0,O["default"])(t,function(t){return t.version>n}),"version");return(0,_["default"])(p,function(t,r){return r.migrate(t)},v.value)}function c(t,r,n){function e(t){if((0,g["default"])(t)){var r=t[0];return"tuple"===r?E.tuple((0,y["default"])((0,d["default"])(t),e)):"array"===r?E.array(e(t[1])):E[r].apply(null,(0,d["default"])(t))}if((0,p["default"])(t)){var n=(0,l["default"])(t).sort();return E.object((0,f["default"])((0,y["default"])(n,function(r){return[r,e(t[r])]})))}}return{version:t,spec:e(r),jsonSpec:r,encodedVersion:(0,P.toVarN)(t),migrate:n||function(t){return t}}}var a=n(122),f=e(a),s=n(16),l=e(s),v=n(5),p=e(v),b=n(133),d=e(b),h=n(10),y=e(h),x=n(1),g=e(x),j=n(48),_=e(j),m=n(118),O=e(m),w=n(132),A=e(w),S=n(119),M=e(S),N=function(){function t(t,r){var n=[],e=!0,o=!1,u=void 0;try{for(var i,c=t[Symbol.iterator]();!(e=(i=c.next()).done)&&(n.push(i.value),!r||n.length!==r);e=!0);}catch(a){o=!0,u=a}finally{try{!e&&c["return"]&&c["return"]()}finally{if(o)throw u}}return n}return function(r,n){if(Array.isArray(r))return r;if(Symbol.iterator in Object(r))return t(r,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();Object.defineProperty(r,"__esModule",{value:!0}),r.register=o,r.encode=u,r.decode=i,r.fromJson=c;var P=n(3),E={}},function(t,r,n){"use strict";function e(t){return t&&t.__esModule?t:{"default":t}}function o(t){return 0===t?1:Math.floor(Math.log(t)/Math.LN2)+1}function u(t,r){var n=t.toString(2);if(n.length>r)throw new Error("Invalid value or bitSize: can't fit "+t+" in "+r+" bits");return(0,j["default"])("0",r-n.length)+n}function i(t){return t&&t.length>=1&&t[0]===_[0]}function c(t){return(0,x["default"])(t,function(t,r){return{bits:t.bits+(r.bits||""),blob:t.blob+(r.blob||"")}},{bits:"",blob:""})}function a(t){if(0>t)throw new Error("Invalid number: can't encode negative number "+t);for(var r="";t>=O;)r=m[t%O]+r,t=Math.floor(t/O);return r=m[t]+r}function f(t){for(var r,n=0,e=0;e31,n=n<<5|31&u}return[n,r]}function l(t){for(var r="",n=Math.ceil(o(t)/5),e=u(t,5*n);e;){var i=e.substr(0,5);e=e.substr(5),i=(0===e.length?"0":"1")+i,r+=p(i)}return r}function v(t,r){var n=a(t);if(n.length>r)throw new Error("Invalid charSize: can't encode "+t+" in "+r+" chars");return(0,j["default"])(m[0],r-n.length)+n}function p(t){for(var r,n="";t;)r=t.substr(0,6),t=t.substr(6),r.length<6&&(r+=(0,j["default"])("0",6-r.length)),n+=a(parseInt(r,2));return n}function b(t,r){return(0,h["default"])(t,function(t){return u(f(t),6)}).join("").substr(0,r)}var d=n(10),h=e(d),y=n(48),x=e(y),g=n(130),j=e(g);Object.defineProperty(r,"__esModule",{value:!0}),r.none=r.notNone=void 0,r.bitsRequired=o,r.paddedBinary=u,r.isNone=i,r.concat=c,r.toN=a,r.fromN=f,r.fromVarN=s,r.toVarN=l,r.paddedN=v,r.bitsToN=p,r.nToBits=b;var _=(r.notNone=u(0,1),r.none=u(1,1)),m="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-",O=m.length},function(t,r,n){var e=n(19),o=n(14),u=e(o,"Map");t.exports=u},function(t,r){function n(t){var r=typeof t;return!!t&&("object"==r||"function"==r)}t.exports=n},function(t,r){function n(t){return!!t&&"object"==typeof t}t.exports=n},function(t,r,n){var e=n(71),o=n(87),u=o(e);t.exports=u},function(t,r,n){function e(t){var r=typeof t;return"function"==r?t:null==t?i:"object"==r?c(t)?u(t[0],t[1]):o(t):a(t)}var o=n(76),u=n(77),i=n(45),c=n(1),a=n(129);t.exports=e},function(t,r,n){function e(t){return null!=t&&!("function"==typeof t&&u(t))&&i(o(t))}var o=n(92),u=n(23),i=n(15);t.exports=e},function(t,r,n){function e(t,r){var n=c(t)?o:i;return n(t,u(r,3))}var o=n(17),u=n(8),i=n(35),c=n(1);t.exports=e},function(t,r,n){function e(t,r){for(var n=t.length;n--;)if(o(t[n][0],r))return n;return-1}var o=n(43);t.exports=e},function(t,r){function n(t){var r=typeof t;return"number"==r||"boolean"==r||"string"==r&&"__proto__"!==t||null==t}t.exports=n},function(t,r,n){var e=n(19),o=e(Object,"create");t.exports=o},function(t,r,n){(function(t,e){var o=n(84),u={"function":!0,object:!0},i=u[typeof r]&&r&&!r.nodeType?r:null,c=u[typeof t]&&t&&!t.nodeType?t:null,a=o(i&&c&&"object"==typeof e&&e),f=o(u[typeof self]&&self),s=o(u[typeof window]&&window),l=o(u[typeof this]&&this),v=a||s!==(l&&l.window)&&s||f||l||Function("return this")();t.exports=v}).call(r,n(136)(t),function(){return this}())},function(t,r){function n(t){return"number"==typeof t&&t>-1&&t%1==0&&e>=t}var e=9007199254740991;t.exports=n},function(t,r,n){function e(t){var r=f(t);if(!r&&!c(t))return u(t);var n=i(t),e=!!n,s=n||[],l=s.length;for(var v in t)!o(t,v)||e&&("length"==v||a(v,l))||r&&"constructor"==v||s.push(v);return s}var o=n(18),u=n(75),i=n(98),c=n(9),a=n(20),f=n(100);t.exports=e},function(t,r){function n(t,r){for(var n=-1,e=t.length,o=Array(e);++n-1&&t%1==0&&r>t}var e=9007199254740991,o=/^(?:0|[1-9]\d*)$/;t.exports=n},function(t,r,n){function e(t,r){return"number"==typeof t?!0:!o(t)&&(i.test(t)||!u.test(t)||null!=r&&t in Object(r))}var o=n(1),u=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,i=/^\w*$/;t.exports=e},function(t,r,n){function e(t){return o(t)&&c.call(t,"callee")&&(!f.call(t,"callee")||a.call(t)==u)}var o=n(46),u="[object Arguments]",i=Object.prototype,c=i.hasOwnProperty,a=i.toString,f=i.propertyIsEnumerable;t.exports=e},function(t,r,n){function e(t){var r=o(t)?a.call(t):"";return r==u||r==i}var o=n(5),u="[object Function]",i="[object GeneratorFunction]",c=Object.prototype,a=c.toString;t.exports=e},function(t,r,n){function e(t){if(!t)return 0===t?t:0;if(t=o(t),t===u||t===-u){var r=0>t?-1:1;return r*i}var n=t%1;return t===t?n?t-n:t:0}var o=n(134),u=1/0,i=1.7976931348623157e308;t.exports=e},function(t,r,n){"use strict";function e(){for(var t=arguments.length,r=Array(t),n=0;t>n;n++)r[n]=arguments[n];var e=(0,o.bitsRequired)(r.length-1);return{encode:function(t){var n=r.indexOf(t);if(-1===n)throw new Error("Invalid choice: "+t+" is not one of "+r.join(","));return{bits:(0,o.paddedBinary)(n,e),blob:""}},decode:function(t){var n=t.bits,o=t.blob,u=parseInt(n.substr(0,e),2);return{value:r[u],rest:{bits:n.substring(e),blob:o}}}}}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=e;var o=n(3),u=n(2);(0,u.register)("oneOf",e)},function(t,r,n){function e(t){var r=-1,n=t?t.length:0;for(this.clear();++rn)return!1;var e=t.length-1;return n==e?t.pop():i.call(t,n,1),!0}var o=n(11),u=Array.prototype,i=u.splice;t.exports=e},function(t,r,n){function e(t,r){var n=o(t,r);return 0>n?void 0:t[n][1]}var o=n(11);t.exports=e},function(t,r,n){function e(t,r){return o(t,r)>-1}var o=n(11);t.exports=e},function(t,r,n){function e(t,r,n){var e=o(t,r);0>e?t.push([r,n]):t[e][1]=n}var o=n(11);t.exports=e},function(t,r,n){function e(t,r,n,a){a||(a=[]);for(var f=-1,s=t.length;++fn;)t=t[r[n++]];return n&&n==e?t:void 0}var o=n(38),u=n(21);t.exports=e},function(t,r,n){function e(t,r,n,c,a){return t===r?!0:null==t||null==r||!u(t)&&!i(r)?t!==t&&r!==r:o(t,r,e,n,c,a)}var o=n(73),u=n(5),i=n(6);t.exports=e},function(t,r,n){function e(t,r){var n=-1,e=u(t)?Array(t.length):[];return o(t,function(t,o,u){e[++n]=r(t,o,u)}),e}var o=n(7),u=n(9);t.exports=e},function(t,r){function n(t){return function(r){return null==r?void 0:r[t]}}t.exports=n},function(t,r){function n(t,r,n){var e=-1,o=t.length;0>r&&(r=-r>o?0:o+r),n=n>o?o:n,0>n&&(n+=o),o=r>n?0:n-r>>>0,r>>>=0;for(var u=Array(o);++es;s++)f=t.decode(l),v[s]=f.value,l=f.rest;return{value:v,rest:l}}}}var u=n(10),i=e(u),c=function(){function t(t,r){var n=[],e=!0,o=!1,u=void 0;try{for(var i,c=t[Symbol.iterator]();!(e=(i=c.next()).done)&&(n.push(i.value),!r||n.length!==r);e=!0);}catch(a){o=!0,u=a}finally{try{!e&&c["return"]&&c["return"]()}finally{if(o)throw u}}return n}return function(r,n){if(Array.isArray(r))return r;if(Symbol.iterator in Object(r))return t(r,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();Object.defineProperty(r,"__esModule",{value:!0}),r.array=o;var a=n(3),f=n(2);(0,f.register)("array",o)},function(t,r,n){"use strict";function e(t){return t&&t.__esModule?t:{"default":t}}function o(){return(0,i["default"])(!0,!1)}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=o;var u=n(25),i=e(u),c=n(2);(0,c.register)("boolean",o)},function(t,r,n){"use strict";function e(t){return{encode:function(t){return{bits:"",blob:t.toString()}},decode:function(r){var n=r.bits,e=r.blob;return{value:e.substr(0,t),rest:{bits:n,blob:e.substr(t)}}}}}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=e;var o=n(2);(0,o.register)("fixedchar",e)},function(t,r,n){"use strict";function e(){return{encode:function(t){var r=Math.abs(t).toString(2),n=(0,o.paddedBinary)(r.length,6)+(t>0?"1":"0")+r;return{bits:n,blob:""}},decode:function(t){var r=t.bits,n=t.blob,e=parseInt(r.substr(0,6),2);r=r.substr(6);var o="1"===r[0]?1:-1;return r=r.substr(1),{value:o*parseInt(r.substr(0,e),2),rest:{bits:r.substr(e),blob:n}}}}}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=e;var o=n(3),u=n(2);(0,u.register)("integer",e)},function(t,r,n){"use strict";function e(t){return t&&t.__esModule?t:{"default":t}}function o(t){return{encode:function(r){return(0,p.concat)((0,v["default"])((0,s["default"])(t,function(t,n){return(0,a["default"])(r,n)?[{bits:p.notNone},t.encode(r[n])]:{bits:p.none}})))},decode:function(r){var n=r.bits,e=r.blob,o={};return(0,i["default"])(t,function(t,r){if((0,p.isNone)(n))return void(n=n.substr(1));n=n.substr(1);var u=t.decode({bits:n,blob:e});n=u.rest.bits,e=u.rest.blob,o[r]=u.value}),{value:o,rest:{bits:n,blob:e}}}}}var u=n(42),i=e(u),c=n(123),a=e(c),f=n(10),s=e(f),l=n(120),v=e(l);Object.defineProperty(r,"__esModule",{value:!0}),r.object=o;var p=n(3),b=n(2);(0,b.register)("object",o)},function(t,r,n){"use strict";function e(t){return t&&t.__esModule?t:{"default":t}}function o(t){return{encode:function(r){return(0,f.concat)((0,a["default"])(t,function(t,n){return t.encode(r[n])}))},decode:function(r){var n=[];return(0,i["default"])(t,function(t,e){var o=t.decode(r);n[e]=o.value,r=o.rest}),{value:n,rest:r}}}}var u=n(42),i=e(u),c=n(10),a=e(c);Object.defineProperty(r,"__esModule",{value:!0}),r.tuple=o;var f=n(3),s=n(2);(0,s.register)("tuple",o)},function(t,r,n){"use strict";function e(){return{encode:function(t){return{bits:"",blob:(0,u.toVarN)(t.length)+t}},decode:function(t){var r,n=t.bits,e=t.blob,i=(0,u.fromVarN)(e),c=o(i,2);return r=c[0],e=c[1],{value:e.substr(0,r),rest:{bits:n,blob:e.substr(r)}}}}}var o=function(){function t(t,r){var n=[],e=!0,o=!1,u=void 0;try{for(var i,c=t[Symbol.iterator]();!(e=(i=c.next()).done)&&(n.push(i.value),!r||n.length!==r);e=!0);}catch(a){o=!0,u=a}finally{try{!e&&c["return"]&&c["return"]()}finally{if(o)throw u}}return n}return function(r,n){if(Array.isArray(r))return r;if(Symbol.iterator in Object(r))return t(r,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=e;var u=n(3),i=n(2);(0,i.register)("varchar",e)},function(t,r,n){function e(){}var o=n(13),u=Object.prototype;e.prototype=o?o(null):u,t.exports=e},function(t,r,n){function e(t){var r=-1,n=t?t.length:0;for(this.clear();++rr&&!u||!o||n&&!i&&c||e&&c)return 1;if(r>t&&!n||!c||u&&!e&&o||i&&o)return-1}return 0}t.exports=n},function(t,r,n){function e(t,r,n){for(var e=-1,u=t.criteria,i=r.criteria,c=u.length,a=n.length;++e=a)return f;var s=n[e];return f*("desc"==s?-1:1)}}return t.index-r.index}var o=n(85);t.exports=e},function(t,r,n){function e(t,r){return function(n,e){if(null==n)return n;if(!o(n))return t(n,e);for(var u=n.length,i=r?u:-1,c=Object(n);(r?i--:++iv))return!1;var b=a.get(t);if(b)return b==r;var d=!0;for(a.set(t,r);++fr?0:r,e)):[]}var o=n(37),u=n(24);t.exports=e},function(t,r,n){function e(t,r){var n=c(t)?o:u;return n(t,i(r,3))}var o=n(63),u=n(67),i=n(8),c=n(1);t.exports=e},function(t,r,n){function e(t,r){if(r=c(r,3),a(t)){var n=i(t,r);return n>-1?t[n]:void 0}return u(t,r,o)}var o=n(7),u=n(68),i=n(69),c=n(8),a=n(1);t.exports=e},function(t,r,n){function e(t){var r=t?t.length:0;return r?o(t):[]}var o=n(32);t.exports=e},function(t,r,n){function e(t,r){return"function"==typeof r&&i(t)?o(t,r):u(t,c(r))}var o=n(62),u=n(7),i=n(1),c=n(116);t.exports=e},function(t,r){function n(t){for(var r=-1,n=t?t.length:0,e={};++rr||r>i)return n;do r%2&&(n+=t),r=c(r/2),t+=t;while(r);return n}var o=n(24),u=n(49),i=9007199254740991,c=Math.floor;t.exports=e},function(t,r,n){function e(t,r){if("function"!=typeof t)throw new TypeError(i);return r=c(void 0===r?t.length-1:u(r),0),function(){for(var n=arguments,e=-1,u=c(n.length-r,0),i=Array(u);++e1&&u(t,r[0],r[1])?r=[]:n>2&&u(r[0],r[1],r[2])&&(r.length=1),o(t,e(r),[])});t.exports=c},function(t,r,n){function e(t){return o(t,1)}var o=n(117);t.exports=e},function(t,r,n){function e(t){if(u(t)){var r=o(t.valueOf)?t.valueOf():t;t=u(r)?r+"":r}if("string"!=typeof t)return 0===t?t:+t;t=t.replace(c,"");var n=f.test(t);return n||s.test(t)?l(t.slice(2),n?2:8):a.test(t)?i:+t}var o=n(23),u=n(5),i=NaN,c=/^\s+|\s+$/g,a=/^[-+]0x[0-9a-f]+$/i,f=/^0b[01]+$/i,s=/^0o[0-7]+$/i,l=parseInt;t.exports=e},function(t,r,n){function e(t){return o(t,u(t))}var o=n(83),u=n(16);t.exports=e},function(t,r){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}}])}); -------------------------------------------------------------------------------- /lib/array.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _map = require("lodash/map"); 4 | 5 | var _map2 = _interopRequireDefault(_map); 6 | 7 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 8 | 9 | Object.defineProperty(exports, "__esModule", { 10 | value: true 11 | }); 12 | exports.array = array; 13 | 14 | var _core = require("./core"); 15 | 16 | var _coder = require("./coder"); 17 | 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 19 | 20 | function array(entry) { 21 | return { 22 | encode: function encode(array) { 23 | return (0, _core.concat)([{ blob: (0, _core.toVarN)(array.length) }].concat((0, _map2.default)(array, entry.encode))); 24 | }, 25 | decode: function decode(_ref) { 26 | var bits = _ref.bits; 27 | var blob = _ref.blob; 28 | 29 | var size; 30 | 31 | var _fromVarN = (0, _core.fromVarN)(blob); 32 | 33 | var _fromVarN2 = _slicedToArray(_fromVarN, 2); 34 | 35 | size = _fromVarN2[0]; 36 | blob = _fromVarN2[1]; 37 | 38 | var rest = { bits: bits, blob: blob }; 39 | var array = [], 40 | result, 41 | i; 42 | for (i = 0; i < size; i++) { 43 | result = entry.decode(rest); 44 | array[i] = result.value; 45 | rest = result.rest; 46 | } 47 | return { value: array, rest: rest }; 48 | } 49 | }; 50 | } 51 | 52 | (0, _coder.register)('array', array); -------------------------------------------------------------------------------- /lib/boolean.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = boolean; 7 | 8 | var _oneOf = require("./oneOf"); 9 | 10 | var _oneOf2 = _interopRequireDefault(_oneOf); 11 | 12 | var _coder = require("./coder"); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | function boolean() { 17 | return (0, _oneOf2.default)(true, false); 18 | } 19 | 20 | (0, _coder.register)('boolean', boolean); -------------------------------------------------------------------------------- /lib/coder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _fromPairs = require("lodash/fromPairs"); 4 | 5 | var _fromPairs2 = _interopRequireDefault(_fromPairs); 6 | 7 | var _keys = require("lodash/keys"); 8 | 9 | var _keys2 = _interopRequireDefault(_keys); 10 | 11 | var _isObject = require("lodash/isObject"); 12 | 13 | var _isObject2 = _interopRequireDefault(_isObject); 14 | 15 | var _tail = require("lodash/tail"); 16 | 17 | var _tail2 = _interopRequireDefault(_tail); 18 | 19 | var _map = require("lodash/map"); 20 | 21 | var _map2 = _interopRequireDefault(_map); 22 | 23 | var _isArray = require("lodash/isArray"); 24 | 25 | var _isArray2 = _interopRequireDefault(_isArray); 26 | 27 | var _reduce = require("lodash/reduce"); 28 | 29 | var _reduce2 = _interopRequireDefault(_reduce); 30 | 31 | var _filter = require("lodash/filter"); 32 | 33 | var _filter2 = _interopRequireDefault(_filter); 34 | 35 | var _sortBy = require("lodash/sortBy"); 36 | 37 | var _sortBy2 = _interopRequireDefault(_sortBy); 38 | 39 | var _find = require("lodash/find"); 40 | 41 | var _find2 = _interopRequireDefault(_find); 42 | 43 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 44 | 45 | Object.defineProperty(exports, "__esModule", { 46 | value: true 47 | }); 48 | exports.register = register; 49 | exports.encode = encode; 50 | exports.decode = decode; 51 | exports.fromJson = fromJson; 52 | 53 | var _core = require("./core"); 54 | 55 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 56 | 57 | var availableTypes = {}; 58 | 59 | function register(name, type) { 60 | availableTypes[name] = type; 61 | } 62 | 63 | function encode(coder, object) { 64 | var _coder$spec$encode = coder.spec.encode(object); 65 | 66 | var bits = _coder$spec$encode.bits; 67 | var blob = _coder$spec$encode.blob; 68 | 69 | return coder.encodedVersion + (0, _core.toVarN)(bits.length) + (0, _core.bitsToN)(bits) + blob; 70 | } 71 | 72 | function decode(coders, string) { 73 | var version, bitSize; 74 | 75 | var _fromVarN = (0, _core.fromVarN)(string); 76 | 77 | var _fromVarN2 = _slicedToArray(_fromVarN, 2); 78 | 79 | version = _fromVarN2[0]; 80 | string = _fromVarN2[1]; 81 | 82 | var _fromVarN3 = (0, _core.fromVarN)(string); 83 | 84 | var _fromVarN4 = _slicedToArray(_fromVarN3, 2); 85 | 86 | bitSize = _fromVarN4[0]; 87 | string = _fromVarN4[1]; 88 | 89 | var coder = (0, _find2.default)(coders, function (c) { 90 | return c.version === version; 91 | }); 92 | if (!coder) { 93 | throw new Error("Invalid version: " + version); 94 | } 95 | 96 | var bitCharSize = Math.ceil(bitSize / 6); 97 | var bits = (0, _core.nToBits)(string.substr(0, bitCharSize), bitSize); 98 | var blob = string.substr(bitCharSize); 99 | var result = coder.spec.decode({ bits: bits, blob: blob }); 100 | var pendingMigrations = (0, _sortBy2.default)((0, _filter2.default)(coders, function (coder) { 101 | return coder.version > version; 102 | }), 'version'); 103 | return (0, _reduce2.default)(pendingMigrations, function (value, coder) { 104 | return coder.migrate(value); 105 | }, result.value); 106 | } 107 | 108 | function fromJson(version, jsonSpec, migrate) { 109 | function loop(spec) { 110 | if ((0, _isArray2.default)(spec)) { 111 | var method = spec[0]; 112 | if (method === 'tuple') { 113 | return availableTypes.tuple((0, _map2.default)((0, _tail2.default)(spec), loop)); 114 | } else if (method === 'array') { 115 | return availableTypes.array(loop(spec[1])); 116 | } else { 117 | return availableTypes[method].apply(null, (0, _tail2.default)(spec)); 118 | } 119 | } else if ((0, _isObject2.default)(spec)) { 120 | var entries = (0, _keys2.default)(spec).sort(); 121 | return availableTypes.object((0, _fromPairs2.default)((0, _map2.default)(entries, function (key) { 122 | return [key, loop(spec[key])]; 123 | }))); 124 | } 125 | } 126 | 127 | return { 128 | version: version, 129 | spec: loop(jsonSpec), 130 | jsonSpec: jsonSpec, 131 | encodedVersion: (0, _core.toVarN)(version), 132 | migrate: migrate || function (x) { 133 | return x; 134 | } 135 | }; 136 | } -------------------------------------------------------------------------------- /lib/core.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _map = require('lodash/map'); 4 | 5 | var _map2 = _interopRequireDefault(_map); 6 | 7 | var _reduce = require('lodash/reduce'); 8 | 9 | var _reduce2 = _interopRequireDefault(_reduce); 10 | 11 | var _repeat = require('lodash/repeat'); 12 | 13 | var _repeat2 = _interopRequireDefault(_repeat); 14 | 15 | Object.defineProperty(exports, "__esModule", { 16 | value: true 17 | }); 18 | exports.none = exports.notNone = undefined; 19 | exports.bitsRequired = bitsRequired; 20 | exports.paddedBinary = paddedBinary; 21 | exports.isNone = isNone; 22 | exports.concat = concat; 23 | exports.toN = toN; 24 | exports.fromN = fromN; 25 | exports.fromVarN = fromVarN; 26 | exports.toVarN = toVarN; 27 | exports.paddedN = paddedN; 28 | exports.bitsToN = bitsToN; 29 | exports.nToBits = nToBits; 30 | 31 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 32 | 33 | function bitsRequired(maxValue) { 34 | if (maxValue === 0) { 35 | return 1; 36 | } 37 | return Math.floor(Math.log(maxValue) / Math.LN2) + 1; 38 | } 39 | 40 | function paddedBinary(value, bitSize) { 41 | var binary = value.toString(2); 42 | if (binary.length > bitSize) { 43 | throw new Error('Invalid value or bitSize: can\'t fit ' + value + ' in ' + bitSize + ' bits'); 44 | } 45 | 46 | return (0, _repeat2.default)('0', bitSize - binary.length) + binary; 47 | } 48 | 49 | var notNone = exports.notNone = paddedBinary(0, 1); 50 | var none = exports.none = paddedBinary(1, 1); 51 | 52 | function isNone(bits) { 53 | return bits && bits.length >= 1 && bits[0] === none[0]; 54 | } 55 | 56 | function concat(encoded) { 57 | return (0, _reduce2.default)(encoded, function (acc, obj) { 58 | return { bits: acc.bits + (obj.bits || ''), blob: acc.blob + (obj.blob || '') }; 59 | }, { bits: '', blob: '' }); 60 | } 61 | 62 | var availableCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-'; 63 | var base = availableCharacters.length; // 64 64 | 65 | function toN(x) { 66 | if (x < 0) { 67 | throw new Error('Invalid number: can\'t encode negative number ' + x); 68 | } 69 | 70 | var result = ''; 71 | while (x >= base) { 72 | result = availableCharacters[x % base] + result; 73 | x = Math.floor(x / base); 74 | } 75 | 76 | result = availableCharacters[x] + result; 77 | return result; 78 | } 79 | 80 | function fromN(n) { 81 | var x = 0, 82 | index; 83 | for (var i = 0; i < n.length; i++) { 84 | index = availableCharacters.indexOf(n[i]); 85 | if (index === -1) { 86 | throw new Error('Invalid number: can\'t decode ' + n); 87 | } 88 | x += index * Math.pow(base, n.length - i - 1); 89 | } 90 | return x; 91 | } 92 | 93 | function fromVarN(string) { 94 | var str = string; 95 | var value = 0; 96 | var hasMore = true; 97 | while (hasMore) { 98 | if (str.length === 0) { 99 | throw new Error('Invalid number: can\'t decode ' + string); 100 | } 101 | var byte = str[0]; 102 | str = str.substr(1); 103 | var n = fromN(byte); 104 | hasMore = n > 31; 105 | value = value << 5 | n & 31; 106 | } 107 | return [value, str]; 108 | } 109 | 110 | function toVarN(n) { 111 | var result = ''; 112 | var charsRequired = Math.ceil(bitsRequired(n) / 5); 113 | var bits = paddedBinary(n, charsRequired * 5); 114 | while (bits) { 115 | var part = bits.substr(0, 5); 116 | bits = bits.substr(5); 117 | part = (bits.length === 0 ? '0' : '1') + part; 118 | result += bitsToN(part); 119 | } 120 | return result; 121 | } 122 | 123 | function paddedN(x, charSize) { 124 | var r = toN(x); 125 | if (r.length > charSize) { 126 | throw new Error('Invalid charSize: can\'t encode ' + x + ' in ' + charSize + ' chars'); 127 | } 128 | 129 | return (0, _repeat2.default)(availableCharacters[0], charSize - r.length) + r; 130 | } 131 | 132 | function bitsToN(bits) { 133 | var result = '', 134 | char; 135 | while (bits) { 136 | char = bits.substr(0, 6); 137 | bits = bits.substr(6); 138 | 139 | if (char.length < 6) { 140 | char += (0, _repeat2.default)('0', 6 - char.length); 141 | } 142 | result += toN(parseInt(char, 2)); 143 | } 144 | 145 | return result; 146 | } 147 | 148 | function nToBits(chars, bitSize) { 149 | return (0, _map2.default)(chars, function (c) { 150 | return paddedBinary(fromN(c), 6); 151 | }).join('').substr(0, bitSize); 152 | } -------------------------------------------------------------------------------- /lib/fixedchar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = fixedChar; 7 | 8 | var _coder = require('./coder'); 9 | 10 | function fixedChar(size) { 11 | return { 12 | encode: function encode(string) { 13 | return { bits: '', blob: string.toString() }; 14 | }, 15 | decode: function decode(_ref) { 16 | var bits = _ref.bits; 17 | var blob = _ref.blob; 18 | 19 | return { 20 | value: blob.substr(0, size), 21 | rest: { bits: bits, blob: blob.substr(size) } 22 | }; 23 | } 24 | }; 25 | } 26 | 27 | (0, _coder.register)('fixedchar', fixedChar); -------------------------------------------------------------------------------- /lib/integer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = integer; 7 | 8 | var _core = require("./core"); 9 | 10 | var _coder = require("./coder"); 11 | 12 | function integer() { 13 | return { 14 | encode: function encode(int) { 15 | var binary = Math.abs(int).toString(2); 16 | var bits = (0, _core.paddedBinary)(binary.length, 6) + (int > 0 ? '1' : '0') + binary; 17 | return { bits: bits, blob: '' }; 18 | }, 19 | decode: function decode(_ref) { 20 | var bits = _ref.bits; 21 | var blob = _ref.blob; 22 | 23 | var size = parseInt(bits.substr(0, 6), 2); 24 | bits = bits.substr(6); 25 | var sign = bits[0] === '1' ? 1 : -1; 26 | bits = bits.substr(1); 27 | return { 28 | value: sign * parseInt(bits.substr(0, size), 2), 29 | rest: { bits: bits.substr(size), blob: blob } 30 | }; 31 | } 32 | }; 33 | } 34 | 35 | (0, _coder.register)('integer', integer); -------------------------------------------------------------------------------- /lib/object.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _each = require("lodash/each"); 4 | 5 | var _each2 = _interopRequireDefault(_each); 6 | 7 | var _has = require("lodash/has"); 8 | 9 | var _has2 = _interopRequireDefault(_has); 10 | 11 | var _map = require("lodash/map"); 12 | 13 | var _map2 = _interopRequireDefault(_map); 14 | 15 | var _flatten = require("lodash/flatten"); 16 | 17 | var _flatten2 = _interopRequireDefault(_flatten); 18 | 19 | Object.defineProperty(exports, "__esModule", { 20 | value: true 21 | }); 22 | exports.object = object; 23 | 24 | var _core = require("./core"); 25 | 26 | var _coder = require("./coder"); 27 | 28 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 29 | 30 | function object(entries) { 31 | return { 32 | encode: function encode(object) { 33 | return (0, _core.concat)((0, _flatten2.default)((0, _map2.default)(entries, function (entry, key) { 34 | if ((0, _has2.default)(object, key)) { 35 | return [{ bits: _core.notNone }, entry.encode(object[key])]; 36 | } 37 | return { bits: _core.none }; 38 | }))); 39 | }, 40 | decode: function decode(_ref) { 41 | var bits = _ref.bits; 42 | var blob = _ref.blob; 43 | 44 | var object = {}; 45 | (0, _each2.default)(entries, function (entry, key) { 46 | if ((0, _core.isNone)(bits)) { 47 | bits = bits.substr(1); 48 | return; 49 | } else { 50 | bits = bits.substr(1); 51 | } 52 | 53 | var result = entry.decode({ bits: bits, blob: blob }); 54 | bits = result.rest.bits; 55 | blob = result.rest.blob; 56 | object[key] = result.value; 57 | }); 58 | return { value: object, rest: { bits: bits, blob: blob } }; 59 | } 60 | }; 61 | } 62 | 63 | (0, _coder.register)('object', object); -------------------------------------------------------------------------------- /lib/oneOf.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = oneOf; 7 | 8 | var _core = require("./core"); 9 | 10 | var _coder = require("./coder"); 11 | 12 | function oneOf() { 13 | for (var _len = arguments.length, choices = Array(_len), _key = 0; _key < _len; _key++) { 14 | choices[_key] = arguments[_key]; 15 | } 16 | 17 | var bitSize = (0, _core.bitsRequired)(choices.length - 1); 18 | return { 19 | encode: function encode(choice) { 20 | var index = choices.indexOf(choice); 21 | if (index === -1) { 22 | throw new Error("Invalid choice: " + choice + " is not one of " + choices.join(',')); 23 | } 24 | return { bits: (0, _core.paddedBinary)(index, bitSize), blob: '' }; 25 | }, 26 | 27 | decode: function decode(_ref) { 28 | var bits = _ref.bits; 29 | var blob = _ref.blob; 30 | 31 | var index = parseInt(bits.substr(0, bitSize), 2); 32 | return { 33 | value: choices[index], 34 | rest: { bits: bits.substring(bitSize), blob: blob } 35 | }; 36 | } 37 | }; 38 | } 39 | 40 | (0, _coder.register)('oneOf', oneOf); -------------------------------------------------------------------------------- /lib/tuple.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _each = require("lodash/each"); 4 | 5 | var _each2 = _interopRequireDefault(_each); 6 | 7 | var _map = require("lodash/map"); 8 | 9 | var _map2 = _interopRequireDefault(_map); 10 | 11 | Object.defineProperty(exports, "__esModule", { 12 | value: true 13 | }); 14 | exports.tuple = tuple; 15 | 16 | var _core = require("./core"); 17 | 18 | var _coder = require("./coder"); 19 | 20 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 21 | 22 | function tuple(entries) { 23 | return { 24 | encode: function encode(array) { 25 | return (0, _core.concat)((0, _map2.default)(entries, function (entry, i) { 26 | return entry.encode(array[i]); 27 | })); 28 | }, 29 | decode: function decode(rest) { 30 | var array = []; 31 | (0, _each2.default)(entries, function (entry, i) { 32 | var result = entry.decode(rest); 33 | array[i] = result.value; 34 | rest = result.rest; 35 | }); 36 | return { value: array, rest: rest }; 37 | } 38 | }; 39 | } 40 | 41 | (0, _coder.register)('tuple', tuple); -------------------------------------------------------------------------------- /lib/u.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.register = exports.decode = exports.encode = exports.fromJson = undefined; 7 | 8 | var _coder = require("./coder"); 9 | 10 | Object.defineProperty(exports, "fromJson", { 11 | enumerable: true, 12 | get: function get() { 13 | return _coder.fromJson; 14 | } 15 | }); 16 | Object.defineProperty(exports, "encode", { 17 | enumerable: true, 18 | get: function get() { 19 | return _coder.encode; 20 | } 21 | }); 22 | Object.defineProperty(exports, "decode", { 23 | enumerable: true, 24 | get: function get() { 25 | return _coder.decode; 26 | } 27 | }); 28 | Object.defineProperty(exports, "register", { 29 | enumerable: true, 30 | get: function get() { 31 | return _coder.register; 32 | } 33 | }); 34 | 35 | require("./oneOf"); 36 | 37 | require("./boolean"); 38 | 39 | require("./integer"); 40 | 41 | require("./varchar"); 42 | 43 | require("./fixedchar"); 44 | 45 | require("./object"); 46 | 47 | require("./tuple"); 48 | 49 | require("./array"); -------------------------------------------------------------------------------- /lib/varchar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports.default = varchar; 9 | 10 | var _core = require("./core"); 11 | 12 | var _coder = require("./coder"); 13 | 14 | function varchar() { 15 | return { 16 | encode: function encode(string) { 17 | return { bits: '', blob: (0, _core.toVarN)(string.length) + string }; 18 | }, 19 | decode: function decode(_ref) { 20 | var bits = _ref.bits; 21 | var blob = _ref.blob; 22 | 23 | var size; 24 | 25 | var _fromVarN = (0, _core.fromVarN)(blob); 26 | 27 | var _fromVarN2 = _slicedToArray(_fromVarN, 2); 28 | 29 | size = _fromVarN2[0]; 30 | blob = _fromVarN2[1]; 31 | 32 | return { 33 | value: blob.substr(0, size), 34 | rest: { bits: bits, blob: blob.substr(size) } 35 | }; 36 | } 37 | }; 38 | } 39 | 40 | (0, _coder.register)('varchar', varchar); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "u-node", 3 | "version": "0.0.2", 4 | "description": "A library to compactly encode data which can be used in URL.", 5 | "main": "lib/u.js", 6 | "scripts": { 7 | "test": "./node_modules/mocha/bin/mocha --compilers js:babel-core/register", 8 | "build": "babel src -d lib", 9 | "dist": "webpack lib/u.js dist/u.js --output-library u --output-library-target umd", 10 | "dist-min": "webpack lib/u.js dist/u.min.js -p --output-library u --output-library-target umd" 11 | }, 12 | "keywords": [ 13 | "url", 14 | "encode", 15 | "compression" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/ananthakumaran/u.git" 20 | }, 21 | "author": "ananthakumaran ", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "babel-cli": "^6.3.17", 25 | "babel-core": "^6.3.26", 26 | "babel-plugin-lodash": "^2.0.1", 27 | "babel-preset-es2015": "^6.3.13", 28 | "jsverify": "^0.7.1", 29 | "mocha": "^2.4.5", 30 | "webpack": "^1.12.11" 31 | }, 32 | "dependencies": { 33 | "lodash": "4.2.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/array.js: -------------------------------------------------------------------------------- 1 | import {concat, fromVarN, toVarN} from "./core"; 2 | import _ from "lodash"; 3 | import {register} from "./coder"; 4 | 5 | export function array(entry) { 6 | return { 7 | encode: function (array) { 8 | return concat([{blob: toVarN(array.length)}].concat(_.map(array, entry.encode))); 9 | }, 10 | decode: function ({bits, blob}) { 11 | var size; 12 | [size, blob] = fromVarN(blob); 13 | var rest = {bits, blob}; 14 | var array = [], result, i; 15 | for (i = 0; i < size; i++) { 16 | result = entry.decode(rest); 17 | array[i] = result.value; 18 | rest = result.rest; 19 | } 20 | return {value: array, rest}; 21 | } 22 | }; 23 | } 24 | 25 | register('array', array); 26 | -------------------------------------------------------------------------------- /src/boolean.js: -------------------------------------------------------------------------------- 1 | import oneOf from "./oneOf"; 2 | import {register} from "./coder"; 3 | 4 | export default function boolean() { 5 | return oneOf(true, false); 6 | } 7 | 8 | register('boolean', boolean); 9 | -------------------------------------------------------------------------------- /src/coder.js: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | import {bitsToN, nToBits, fromVarN, toVarN} from "./core"; 3 | 4 | var availableTypes = {}; 5 | 6 | export function register(name, type) { 7 | availableTypes[name] = type; 8 | } 9 | 10 | export function encode(coder, object) { 11 | var {bits, blob} = coder.spec.encode(object); 12 | return coder.encodedVersion + toVarN(bits.length) + bitsToN(bits) + blob; 13 | } 14 | 15 | export function decode(coders, string) { 16 | var version, bitSize; 17 | [version, string] = fromVarN(string); 18 | [bitSize, string] = fromVarN(string); 19 | 20 | var coder = _.find(coders, c => c.version === version); 21 | if (!coder) { 22 | throw new Error(`Invalid version: ${version}`); 23 | } 24 | 25 | var bitCharSize = Math.ceil(bitSize / 6); 26 | var bits = nToBits(string.substr(0, bitCharSize), bitSize); 27 | var blob = string.substr(bitCharSize); 28 | var result = coder.spec.decode({bits, blob}); 29 | var pendingMigrations = _.sortBy(_.filter(coders, coder => coder.version > version), 'version'); 30 | return _.reduce(pendingMigrations, (value, coder) => coder.migrate(value), result.value); 31 | } 32 | 33 | export function fromJson(version, jsonSpec, migrate) { 34 | function loop(spec) { 35 | if (_.isArray(spec)) { 36 | var method = spec[0]; 37 | if (method === 'tuple') { 38 | return availableTypes.tuple(_.map(_.tail(spec), loop)); 39 | } else if (method === 'array') { 40 | return availableTypes.array(loop(spec[1])); 41 | } else { 42 | return availableTypes[method].apply(null, _.tail(spec)); 43 | } 44 | } else if (_.isObject(spec)) { 45 | var entries = _.keys(spec).sort(); 46 | return availableTypes.object(_.fromPairs(_.map(entries, function (key) { 47 | return [key, loop(spec[key])]; 48 | }))); 49 | } 50 | } 51 | 52 | return { 53 | version: version, 54 | spec: loop(jsonSpec), 55 | jsonSpec: jsonSpec, 56 | encodedVersion: toVarN(version), 57 | migrate: migrate || (x => x) 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | 3 | export function bitsRequired(maxValue) { 4 | if (maxValue === 0) { 5 | return 1; 6 | } 7 | return Math.floor(Math.log(maxValue) / Math.LN2) + 1; 8 | } 9 | 10 | export function paddedBinary(value, bitSize) { 11 | var binary = value.toString(2); 12 | if (binary.length > bitSize) { 13 | throw new Error(`Invalid value or bitSize: can't fit ${value} in ${bitSize} bits`); 14 | } 15 | 16 | return _.repeat('0', bitSize - binary.length) + binary; 17 | } 18 | 19 | export var notNone = paddedBinary(0, 1); 20 | export var none = paddedBinary(1, 1); 21 | 22 | export function isNone(bits) { 23 | return (bits && bits.length >= 1 && bits[0] === none[0]); 24 | } 25 | 26 | export function concat(encoded) { 27 | return _.reduce(encoded, function (acc, obj) { 28 | return {bits: acc.bits + (obj.bits || ''), blob: acc.blob + (obj.blob || '')}; 29 | }, {bits: '', blob: ''}); 30 | } 31 | 32 | var availableCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-'; 33 | var base = availableCharacters.length; // 64 34 | 35 | export function toN(x) { 36 | if (x < 0) { 37 | throw new Error(`Invalid number: can't encode negative number ${x}`); 38 | } 39 | 40 | var result = ''; 41 | while (x >= base) { 42 | result = availableCharacters[x % base] + result; 43 | x = Math.floor(x / base); 44 | } 45 | 46 | result = availableCharacters[x] + result; 47 | return result; 48 | } 49 | 50 | export function fromN(n) { 51 | var x = 0, 52 | index; 53 | for (var i = 0; i < n.length; i++) { 54 | index = availableCharacters.indexOf(n[i]); 55 | if (index === -1) { 56 | throw new Error(`Invalid number: can't decode ${n}`); 57 | } 58 | x += index * Math.pow(base, n.length - i - 1); 59 | } 60 | return x; 61 | } 62 | 63 | export function fromVarN(string) { 64 | var str = string; 65 | var value = 0; 66 | var hasMore = true; 67 | while (hasMore) { 68 | if (str.length === 0) { 69 | throw new Error(`Invalid number: can't decode ${string}`); 70 | } 71 | var byte = str[0]; 72 | str = str.substr(1); 73 | var n = fromN(byte); 74 | hasMore = n > 31; 75 | value = (value << 5) | (n & 31); 76 | } 77 | return [value, str]; 78 | } 79 | 80 | export function toVarN(n) { 81 | var result = ''; 82 | var charsRequired = Math.ceil(bitsRequired(n) / 5); 83 | var bits = paddedBinary(n, charsRequired * 5); 84 | while (bits) { 85 | var part = bits.substr(0, 5); 86 | bits = bits.substr(5); 87 | part = (bits.length === 0 ? '0' : '1') + part; 88 | result += bitsToN(part); 89 | } 90 | return result; 91 | } 92 | 93 | 94 | export function paddedN(x, charSize) { 95 | var r = toN(x); 96 | if (r.length > charSize) { 97 | throw new Error(`Invalid charSize: can't encode ${x} in ${charSize} chars`); 98 | } 99 | 100 | return _.repeat(availableCharacters[0], charSize - r.length) + r; 101 | } 102 | 103 | export function bitsToN(bits) { 104 | var result = '', char; 105 | while (bits) { 106 | char = bits.substr(0, 6); 107 | bits = bits.substr(6); 108 | 109 | if (char.length < 6) { 110 | char += _.repeat('0', 6 - char.length); 111 | } 112 | result += toN(parseInt(char, 2)); 113 | } 114 | 115 | return result; 116 | } 117 | 118 | export function nToBits(chars, bitSize) { 119 | return _.map(chars, c => paddedBinary(fromN(c), 6)).join('').substr(0, bitSize); 120 | } 121 | -------------------------------------------------------------------------------- /src/fixedchar.js: -------------------------------------------------------------------------------- 1 | import {register} from "./coder"; 2 | 3 | export default function fixedChar(size) { 4 | return { 5 | encode: function (string) { 6 | return {bits: '', blob: string.toString()}; 7 | }, 8 | decode: function ({bits, blob}) { 9 | return { 10 | value: blob.substr(0, size), 11 | rest: { bits, blob: blob.substr(size) } 12 | }; 13 | } 14 | }; 15 | } 16 | 17 | register('fixedchar', fixedChar); 18 | -------------------------------------------------------------------------------- /src/integer.js: -------------------------------------------------------------------------------- 1 | import {paddedBinary} from "./core"; 2 | import {register} from "./coder"; 3 | 4 | export default function integer() { 5 | return { 6 | encode: function (int) { 7 | var binary = Math.abs(int).toString(2); 8 | var bits = paddedBinary(binary.length, 6) + (int > 0 ? '1' : '0') + binary; 9 | return {bits, blob: ''}; 10 | }, 11 | decode: function ({bits, blob}) { 12 | var size = parseInt(bits.substr(0, 6), 2); 13 | bits = bits.substr(6); 14 | var sign = bits[0] === '1' ? 1 : -1; 15 | bits = bits.substr(1); 16 | return { 17 | value: sign * parseInt(bits.substr(0, size), 2), 18 | rest: { bits: bits.substr(size), blob } 19 | }; 20 | } 21 | }; 22 | } 23 | 24 | register('integer', integer); 25 | -------------------------------------------------------------------------------- /src/object.js: -------------------------------------------------------------------------------- 1 | import {concat, isNone, none, notNone} from "./core"; 2 | import _ from "lodash"; 3 | import {register} from "./coder"; 4 | 5 | export function object(entries) { 6 | return { 7 | encode: function (object) { 8 | return concat( 9 | _.flatten(_.map(entries, function (entry, key) { 10 | if (_.has(object, key)) { 11 | return [{bits: notNone}, entry.encode(object[key])]; 12 | } 13 | return {bits: none}; 14 | }))); 15 | }, 16 | decode: function ({bits, blob}) { 17 | var object = {}; 18 | _.each(entries, function (entry, key) { 19 | if (isNone(bits)) { 20 | bits = bits.substr(1); 21 | return; 22 | } else { 23 | bits = bits.substr(1); 24 | } 25 | 26 | var result = entry.decode({bits, blob}); 27 | bits = result.rest.bits; 28 | blob = result.rest.blob; 29 | object[key] = result.value; 30 | }); 31 | return { value: object, rest: {bits, blob} }; 32 | } 33 | }; 34 | } 35 | 36 | register('object', object); 37 | -------------------------------------------------------------------------------- /src/oneOf.js: -------------------------------------------------------------------------------- 1 | import {paddedBinary, bitsRequired} from "./core"; 2 | import {register} from "./coder"; 3 | 4 | export default function oneOf(...choices) { 5 | var bitSize = bitsRequired(choices.length - 1); 6 | return { 7 | encode: function (choice) { 8 | var index = choices.indexOf(choice); 9 | if (index === -1) { 10 | throw new Error(`Invalid choice: ${choice} is not one of ${choices.join(',')}`); 11 | } 12 | return {bits: paddedBinary(index, bitSize), blob: ''}; 13 | }, 14 | 15 | decode: function ({bits, blob}) { 16 | var index = parseInt(bits.substr(0, bitSize), 2); 17 | return { 18 | value: choices[index], 19 | rest: { bits: bits.substring(bitSize), blob } 20 | }; 21 | } 22 | }; 23 | } 24 | 25 | register('oneOf', oneOf); 26 | -------------------------------------------------------------------------------- /src/tuple.js: -------------------------------------------------------------------------------- 1 | import {concat} from "./core"; 2 | import _ from "lodash"; 3 | import {register} from "./coder"; 4 | 5 | export function tuple(entries) { 6 | return { 7 | encode: function (array) { 8 | return concat(_.map(entries, (entry, i) => entry.encode(array[i]))); 9 | }, 10 | decode: function (rest) { 11 | var array = []; 12 | _.each(entries, (entry, i) => { 13 | var result = entry.decode(rest); 14 | array[i] = result.value; 15 | rest = result.rest; 16 | }); 17 | return {value: array, rest}; 18 | } 19 | }; 20 | } 21 | 22 | register('tuple', tuple); 23 | -------------------------------------------------------------------------------- /src/u.js: -------------------------------------------------------------------------------- 1 | export {fromJson, encode, decode, register} from "./coder"; 2 | import "./oneOf"; 3 | import "./boolean"; 4 | import "./integer"; 5 | import "./varchar"; 6 | import "./fixedchar"; 7 | import "./object"; 8 | import "./tuple"; 9 | import "./array"; 10 | -------------------------------------------------------------------------------- /src/varchar.js: -------------------------------------------------------------------------------- 1 | import {fromVarN, toVarN} from "./core"; 2 | import {register} from "./coder"; 3 | 4 | export default function varchar() { 5 | return { 6 | encode: function (string) { 7 | return {bits: '', blob: toVarN(string.length) + string}; 8 | }, 9 | decode: function ({bits, blob}) { 10 | var size; 11 | [size, blob] = fromVarN(blob); 12 | return { 13 | value: blob.substr(0, size), 14 | rest: { bits, blob: blob.substr(size) } 15 | }; 16 | } 17 | }; 18 | } 19 | 20 | register('varchar', varchar); 21 | -------------------------------------------------------------------------------- /test/u.js: -------------------------------------------------------------------------------- 1 | import {fromJson, encode, decode} from "../src/u"; 2 | import {nToBits, bitsToN, fromN, toN, fromVarN, toVarN, paddedBinary, paddedN} from "../src/core"; 3 | import jsc from "jsverify"; 4 | import _ from "lodash"; 5 | import util from "util"; 6 | import assert from "assert"; 7 | 8 | var oneOf = jsc.nearray(jsc.json).smap((array) => { 9 | var r = jsc.random(0, array.length - 1); 10 | return [['oneOf'].concat(array), array[r]]; 11 | }, (x) => _.tail(x[0])); 12 | 13 | var boolean = jsc.bool.smap(bool => { 14 | return [['boolean'], bool]; 15 | }, ([spec, value]) => value); 16 | 17 | var integer = jsc.integer.smap(n => { 18 | return [['integer'], n]; 19 | }, ([spec, value]) => value); 20 | 21 | var varchar = jsc.string.smap(x => { 22 | return [['varchar'], x]; 23 | }, ([spec, value]) => value); 24 | 25 | var fixedchar = jsc.string.smap(x => { 26 | return [['fixedchar', x.length], x]; 27 | }, ([spec, value]) => value); 28 | 29 | var wrap = (object) => { 30 | var spec = {}, sample = {}; 31 | _.each(object, (value, key) => { 32 | spec[key] = value[0]; 33 | if (jsc.random(0, 5) !== 0) { 34 | sample[key] = value[1]; 35 | } 36 | }); 37 | return [spec, sample]; 38 | }; 39 | 40 | var unwrap = (value) => { 41 | var spec = value[0], sample = value[1]; 42 | var result = {}; 43 | _.each(spec, (value, key) => { 44 | result[key] = [value, sample[key]]; 45 | }); 46 | return result; 47 | }; 48 | 49 | var wrapTuple = (array) => { 50 | return [['tuple'].concat(_.map(array, ([spec, value]) => spec)), _.map(array, ([spec, value]) => value)]; 51 | }; 52 | 53 | var unwrapTuple = (wrapped) => { 54 | return _.map(_.tail(wrapped[0]), (spec, i) => [spec, wrapped[1][i]]); 55 | }; 56 | 57 | var wrapArray = ([spec, value]) => { 58 | return [['array', spec], _.times(jsc.random(1, 20), _.constant(value))]; 59 | }; 60 | 61 | var unwrapArray = (wrapped) => { 62 | return [wrapped[0][1], wrapped[1][0]]; 63 | }; 64 | 65 | var generateObject = jsc.generator.recursive( 66 | jsc.generator.oneof([oneOf.generator, boolean.generator, integer.generator, varchar.generator, fixedchar.generator]), 67 | function (gen) { 68 | return jsc.generator.oneof([jsc.generator.dict(gen).map(wrap), jsc.generator.nearray(gen).map(wrapTuple), gen.map(wrapArray)]); 69 | } 70 | ); 71 | 72 | var shrinkObject = jsc.shrink.bless((value) => { 73 | var spec = value[0]; 74 | if (_.isArray(spec)) { 75 | var type = spec[0]; 76 | switch (type) { 77 | case 'oneOf': return oneOf.shrink(value); 78 | case 'boolean': return boolean.shrink(value); 79 | case 'tuple': return jsc.shrink.nearray(shrinkObject)(unwrapTuple(value)).map(wrapTuple); 80 | case 'array': return shrinkObject(unwrapArray(value)).map(wrapArray); 81 | case 'integer': return integer.shrink(value); 82 | case 'varchar': return varchar.shrink(value); 83 | case 'fixedchar': return fixedchar.shrink(value); 84 | default: throw new Error(`Invalid type ${type}`); 85 | } 86 | } else { 87 | return shrinkDictObject(unwrap(value)).map(wrap); 88 | } 89 | }); 90 | 91 | var shrinkDictObject = (() => { 92 | var pairShrink = jsc.shrink.pair(jsc.string.shrink, shrinkObject); 93 | var arrayShrink = jsc.shrink.array(pairShrink); 94 | 95 | return arrayShrink.smap(jsc.utils.pairArrayToDict, jsc.utils.dictToPairArray); 96 | })(); 97 | 98 | var object = jsc.bless({ 99 | generator: generateObject, 100 | shrink: shrinkObject, 101 | show: jsc.show 102 | }); 103 | 104 | function validate(generator, debug) { 105 | return function () { 106 | this.timeout(Infinity); 107 | jsc.assert(jsc.forall(generator, (x) => { 108 | var [spec, value] = x; 109 | var coder = fromJson(1, spec); 110 | var encoded = encode(coder, value); 111 | var decoded = decode([coder], encoded); 112 | if (!_.isEqual(value, decoded)) { 113 | console.log('spec ', spec); 114 | console.log('value', util.inspect(value, {depth: null})); 115 | console.log('encoded', encoded); 116 | console.log('decoded', util.inspect(decoded, {depth: null})); 117 | } 118 | return _.isEqual(decoded, value); 119 | })); 120 | }; 121 | } 122 | 123 | function validateExample(spec, value) { 124 | return () => { 125 | var coder = fromJson(1, spec); 126 | var encoded = encode(coder, value); 127 | var decoded = decode([coder], encoded); 128 | if (!_.isEqual(value, decoded)) { 129 | console.log('spec ', spec); 130 | console.log('value', util.inspect(value, {depth: null})); 131 | console.log('encoded', encoded); 132 | console.log('decoded', util.inspect(decoded, {depth: null})); 133 | } 134 | 135 | assert.deepEqual(value, decoded); 136 | }; 137 | } 138 | 139 | describe('u', () => { 140 | describe('primitives', () => { 141 | it('oneOf', validate(oneOf)); 142 | it('boolean', validate(boolean)); 143 | it('number', validate(integer)); 144 | it('varchar', validate(varchar)); 145 | it('fixedchar', validate(fixedchar)); 146 | it('object', validate(object)); 147 | it('tuple', validateExample(['tuple', ['integer'], ['boolean']], [0, true])); 148 | it('array', () => { 149 | validateExample(['array', ['integer']], [0, 1, 3, 4])(); 150 | validateExample(['array', ['integer']], [])(); 151 | validateExample(['array', ['varchar']], ['aasdfas', 'asasasd', 'asdasd'])(); 152 | validateExample(['array', ['varchar']], ['', '', 'asdasd'])(); 153 | }); 154 | 155 | it('should handle unspecified keys', () => { 156 | validateExample({'a': {'a': ['boolean']}}, {})(); 157 | validateExample({'a': {'a': ['boolean']}}, {a: {a: false}})(); 158 | validateExample({'a': {'a': ['boolean'], 'b': ['boolean']}}, {a: {b: false}})(); 159 | }); 160 | }); 161 | 162 | describe('core', () => { 163 | it('should pad numbers', () => { 164 | jsc.assert(jsc.forall("nat", (x) => { 165 | return _.isEqual(parseInt(paddedBinary(x, 64), 2), x); 166 | })); 167 | }); 168 | 169 | it('should encode decode bits', () => { 170 | jsc.assert(jsc.forall("nearray nat", (xs) => { 171 | var bits = _.map(xs, x => x.toString(2)).join(''); 172 | return _.isEqual(nToBits(bitsToN(bits), bits.length), bits); 173 | })); 174 | }); 175 | 176 | it('should encode decode numbers', () => { 177 | assert.equal(fromN(toN(0)), 0); 178 | assert.equal(fromN(toN(63)), 63); 179 | assert.equal(fromN(toN(64)), 64); 180 | assert.equal(fromN(toN(65)), 65); 181 | jsc.assert(jsc.forall('nat', (n) => { 182 | return _.isEqual(fromN(toN(n)), n); 183 | })); 184 | }); 185 | 186 | it('should encode decode numbers in variable length', () => { 187 | assert.equal(fromVarN(toVarN(0))[0], 0); 188 | assert.equal(fromVarN(toVarN(31))[0], 31); 189 | assert.equal(fromVarN(toVarN(32))[0], 32); 190 | assert.equal(fromVarN(toVarN(33))[0], 33); 191 | jsc.assert(jsc.forall('nat', (n) => { 192 | return _.isEqual(fromVarN(toVarN(n))[0], n); 193 | })); 194 | }); 195 | }); 196 | 197 | describe('version', function () { 198 | it('picks the correct version', function () { 199 | var version1 = fromJson(1, {a: ['integer']}), 200 | version2 = fromJson(2, {a: ['boolean']}), 201 | version3 = fromJson(3, {d: ['fixedchar', 2]}); 202 | 203 | var coders = [version1, version2, version3]; 204 | assert.deepEqual(decode(coders, encode(version1, {a: 10})), {a: 10}); 205 | assert.deepEqual(decode(coders, encode(version2, {a: true})), {a: true}); 206 | assert.deepEqual(decode(coders, encode(version3, {d: 'he'})), {d: 'he'}); 207 | }); 208 | }); 209 | 210 | describe('example', () => { 211 | it('works', () => { 212 | // used in README 213 | var spec = { 214 | lookingFor: ['oneOf', 'bride', 'groom'], 215 | age: ['tuple', ['integer'] /* min */, ['integer'] /* max */], 216 | religion: ['oneOf', 'Hindu', 'Muslim', 'Christian', 'Sikh', 'Parsi', 'Jain', 'Buddhist', 'Jewish', 'No Religion', 'Spiritual', 'Other'], 217 | motherTongue: ['oneOf', 'Assamese', 'Bengali', 'English', 'Gujarati', 'Hindi', 'Kannada', 'Konkani', 'Malayalam', 'Marathi', 'Marwari', 'Odia', 'Punjabi', 'Sindhi', 'Tamil', 'Telugu', 'Urdu'], 218 | onlyProfileWithPhoto: ['boolean'] 219 | }; 220 | 221 | var v1 = fromJson(1, spec); 222 | 223 | var encodedv1 = encode(v1, {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true}); 224 | assert.equal(encodedv1, 'bHhc9I-aqa'); 225 | assert.deepEqual(decode([v1], encodedv1), {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true}); 226 | 227 | var newSpec = _.extend({}, spec, { 228 | maritialStatus: ['oneOf', "Doesn't Matter", 'Never Married', 'Divorced', 'Widowed', 'Awaiting Divorce', 'Annulled'] 229 | }); 230 | 231 | var v2 = fromJson(2, newSpec, function (old) { 232 | old.maritialStatus = "Doesn't Matter"; 233 | return old; 234 | }); 235 | 236 | assert.deepEqual(decode([v1, v2], encodedv1), {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true, maritialStatus: "Doesn't Matter"}); 237 | var encodedv2 = encode(v2, {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true, maritialStatus: 'Never Married'}); 238 | assert.equal(encodedv2, 'cHlc9I-aHaa'); 239 | assert.deepEqual(decode([v1, v2], encodedv2), {lookingFor: 'bride', age: [25, 30], religion: 'Hindu', motherTongue: 'Bengali', onlyProfileWithPhoto: true, maritialStatus: 'Never Married'}); 240 | }); 241 | }); 242 | }); 243 | --------------------------------------------------------------------------------