nonvalid
Version:
Simple callback-based JSON validator for complex use-cases
2 lines (1 loc) • 5.95 kB
JavaScript
(function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):(a=a||self,a.nonvalid=b())})(this,function(){'use strict';function a(){let a,h,i,j,k,l,m,n,o;const p=(a=!0)=>{m={},n=!1,o=a},q=(b=!0)=>{a=[],h=[],i=[],j=null,k=0,l=0,p(b)};q(!1);const r=a=>{throw q(),new Error(a)},s=(b,c)=>{a.push(c);let d;try{d=b(c,E())||!1}catch(a){throw o||q(),a}if(o)throw new Error("Cannot proceed with validation after an error");return a.pop(),d},t=a=>{const e=a[c.other];b.undefined(e)||b.function(e)||r("The catch-other callback must be a function");const f=a[c.error];b.defined(f)&&(b.function(f)||!f)&&r("The shape error must be a non-function truthy value");for(const b of Object.getOwnPropertySymbols(a))b!==c.other&&b!==c.error&&d.includes(b)&&r(`${b.toString()} is not expected in an object schema`);return[e,f]},u=(a,c)=>{const[e,h]=t(a);if(!b.object(c))return h||!0;for(const[e,f]of g(a)){if(b.symbol(e)&&d.includes(e))continue;const a=z(f,c[e],e);if(a)return a}for(const[d,h]of g(c))if(!f(a,d)){if(b.undefined(e))return!0;const a=z(e,h,d);if(a)return a}return!1},v=a=>{for(const e of a)b.symbol(e)&&e!==c.end&&d.includes(e)&&r(`${e.toString()} is not expected in an array schema`);let e=0;for(;f(a,e)&&a[e]!==c.end;)e++;a.length>e+3&&r("Found more than 2 elements after the end-of-array marker");let g,h;for(let d=e+1;d<a.length;d++){f(a,d)&&a[d]!==c.end||r("Encountered multiple end-of-array markers");const e=a[d];b.function(e)?(b.defined(g)&&r("Encountered multiple catch-other callbacks"),g=e):e?(b.defined(h)&&r("Encountered multiple shape error values"),h=e):r("Shape error must be a truthy value")}return[e,g,h]},w=(a,c)=>{const[d,e,f]=v(a);if(!b.array(c))return f||!0;for(let b=0;b<d;b++){const d=z(a[b],c[b],b);if(d)return d}for(let f=d;f<c.length;f++){if(b.undefined(e))return!0;const a=z(e,c[f],f);if(a)return a}return!1},x=(a,b,c)=>{h.push(c);const d=a(b,c);return h.pop(),d},y=(a,c)=>b.function(a)?s(a,c):b.object(a)?x(u,a,c):b.array(a)?x(w,a,c):c!==a,z=(a,b,c)=>{i.push(c);const d=A(a,b);return i.pop(),d},A=(a,b)=>{const c=y(a,b);return c?!j&&(j=[...i]):j=null,c},B=(...b)=>{0===k&&(o&&r("To validate another value, use nonvalid.instance()"),n=!0),k++;let c,d;1<b.length?[d,c]=b:(0===a.length&&r("Validator called with no value outside of any context"),c=b[0],d=D());const e=A(c,d);return k--,0===k&&p(),e},C=a=>{if(0<l){const c=setTimeout(()=>{throw new Error("Value created in safe context was not consumed by any matcher")},0),d=a=>new Proxy({},{get:function(g,h){if(h===e.unwrap)return clearTimeout(c),a;if(h===Symbol.toPrimitive){clearTimeout(c);const b=Symbol("nonvalid.safe"),d=setTimeout(()=>{throw new Error("Value created in safe context was used improperly")},0);return m[b]={value:a,timeout:d},()=>b}let i=h;f(m,h)&&(i=m[h].value,clearTimeout(m[h].timeout));const j=(b.object(a)&&(b.symbol(i)||b.string(i)||b.number(i))||b.array(a)&&(b.string(i)&&i.match(/^\d+$/)||b.number(i)))&&f(a,i);return d(j?a[i]:void 0)},getOwnPropertyDescriptor:(a,b)=>{if(b===e.unwrap)return{configurable:!0,enumerable:!1}}});return d(a)}return a};B.root=()=>(0===h.length&&r("root() called outside of any object or array"),C(h[0])),B.up=(a=0)=>(h.length<=a&&r("up() call navigates above any object or array"),C(h[h.length-1-a]));const D=()=>a[a.length-1];B.value=()=>(0===a.length&&r("value() called outside of any context"),C(D()));const E=()=>0===i.length?void 0:i[i.length-1];B.key=()=>{const a=E();return b.undefined(a)&&r("key() called outside of any context"),b.string(a)||b.symbol(a)||r("key() can be called for objects only"),a},B.index=()=>{const a=E();return b.undefined(a)&&r("index() called outside of any context"),b.number(a)||r("index() can be called for arrays only"),a};const F=(a,c)=>{return c?c+a.map(a=>b.symbol(a)?`[${a.toString()}]`:`[${JSON.stringify(a)}]`).join(""):[...a]};B.path=a=>{if(o)throw new Error("path() called after validation");if(!n)throw new Error("path() called before validation");return F(i,a)},B.errorPath=a=>{if(!o)throw new Error("errorPath() called before validation is completed");return j?F(j,a):j};const G=(c,d)=>(...g)=>{if(1<g.length)throw new Error("Matchers are supposed to be run with exactly one or no arguments");else{if(1===g.length){const d=g[0];if(0<a.length&&b.function(d)&&D()!==d){l++;let a;try{a=d()}finally{l--}return b.object(a)&&f(a,e.unwrap)||r("Callback didn\u2019t perform navigation or didn\u2019t return its result"),c(a[e.unwrap])}return c(d)}if(0===a.length)throw new Error(`${d}() called without arguments outside of any context`);return c(D())}};for(const[a,c]of g(b))B[a]=G(c,a);B.addMatcher=(...a)=>{let c,d;switch(a.length){case 2:if([d,c]=a,!b.string(d)&&!b.symbol(d))throw new Error("Name of a matcher should be either string or symbol");if(!b.function(c))throw new Error("Second addMatcher argument must be a function");break;case 1:if(c=a[0],!b.function(c)||!c.name)throw new Error("Single addMatcher argument must be a named function");d=c.name;break;default:throw new Error("addMatcher expects exactly one or two arguments");}if(1!==c.length)throw new Error("Matcher must accept exactly one parameter");if(f(B,d))throw new Error(`Validator already has property "${d.toString()}"`);B[d]=G(c,d)};for(const[a,b]of g(c))B[a]=b;return B}const b={number:a=>"number"==typeof a&&isFinite(a),string:a=>"string"==typeof a,boolean:a=>"boolean"==typeof a,null:a=>null===a,undefined:a=>a===void 0,defined:a=>a!==void 0,bigint:a=>"bigint"==typeof a,symbol:a=>"symbol"==typeof a,function:a=>"function"==typeof a,array:a=>Array.isArray(a),object:a=>"object"==typeof a&&null!==a&&!Array.isArray(a),get:a=>a},c={other:Symbol("nonvalid.other"),error:Symbol("nonvalid.error"),end:Symbol("nonvalid.end")},d=Object.values(c),e={unwrap:Symbol("nonvalid.unwrap")},f=(a,b)=>Object.hasOwnProperty.call(a,b),g=a=>[...Object.entries(a),...Object.getOwnPropertySymbols(a).map(b=>[b,a[b]])],h=a();h.instance=a;return h});