diff --git a/cjs/deserialize.js b/cjs/deserialize.js index 225a6d3..cb84880 100644 --- a/cjs/deserialize.js +++ b/cjs/deserialize.js @@ -91,6 +91,7 @@ const deserializer = ($, _) => { const { buffer } = new Uint8Array(value); return as(new DataView(buffer), value); } + case '-0': return -0; } return as(guard(type, value), index); }; diff --git a/cjs/serialize.js b/cjs/serialize.js index 464c4ac..45bce6e 100644 --- a/cjs/serialize.js +++ b/cjs/serialize.js @@ -6,7 +6,7 @@ const { const EMPTY = ''; const {toString} = {}; -const {keys} = Object; +const {keys, is} = Object; const typeOf = value => { const type = typeof value; @@ -66,6 +66,10 @@ const serializer = (strict, json, $, _) => { TYPE = BIGINT; entry = value.toString(); break; + case 'number': + if (!value && is(value, -0)) + return _.push(['-0']) - 1; + break; case 'function': case 'symbol': if (strict) diff --git a/esm/deserialize.js b/esm/deserialize.js index 349e850..86920f0 100644 --- a/esm/deserialize.js +++ b/esm/deserialize.js @@ -93,6 +93,7 @@ const deserializer = ($, _) => { const { buffer } = new Uint8Array(value); return as(new DataView(buffer), value); } + case '-0': return -0; } return as(guard(type, value), index); }; diff --git a/esm/serialize.js b/esm/serialize.js index 4351d3e..6217735 100644 --- a/esm/serialize.js +++ b/esm/serialize.js @@ -8,7 +8,7 @@ import { const EMPTY = ''; const {toString} = {}; -const {keys} = Object; +const {keys, is} = Object; const typeOf = value => { const type = typeof value; @@ -68,6 +68,10 @@ const serializer = (strict, json, $, _) => { TYPE = BIGINT; entry = value.toString(); break; + case 'number': + if (!value && is(value, -0)) + return _.push(['-0']) - 1; + break; case 'function': case 'symbol': if (strict) diff --git a/structured-json.js b/structured-json.js index 510c686..4fa6359 100644 --- a/structured-json.js +++ b/structured-json.js @@ -1 +1 @@ -var StructuredJSON=function(e){"use strict";const{defineProperty:r}=Object,t="object"==typeof self?self:globalThis,n=(e,r)=>{switch(e){case"Function":case"SharedWorker":case"Worker":case"eval":case"setInterval":case"setTimeout":throw new TypeError("unable to deserialize "+e)}return new t[e](r)},s=e=>((e,s)=>{const c=(r,t)=>(e.set(t,r),r),a=o=>{if(e.has(o))return e.get(o);const[u,i]=s[o];switch(u){case 0:case-1:return c(i,o);case 1:{const e=c([],o);for(const r of i)e.push(a(r));return e}case 2:{const e=c({},o);for(const[t,n]of i){const s=a(t),c=a(n);"__proto__"===s?r(e,s,{value:c,configurable:!0,enumerable:!0,writable:!0}):e[s]=c}return e}case 3:return c(new Date(i),o);case 4:{const{source:e,flags:r}=i;return c(new RegExp(e,r),o)}case 5:{const e=c(new Map,o);for(const[r,t]of i)e.set(a(r),a(t));return e}case 6:{const e=c(new Set,o);for(const r of i)e.add(a(r));return e}case 7:{const{name:e,message:r}=i;return c("function"==typeof t[e]?n(e,r):new Error(r),o)}case 8:return c(BigInt(i),o);case"BigInt":return c(Object(BigInt(i)),o);case"ArrayBuffer":return c(new Uint8Array(i).buffer,i);case"DataView":{const{buffer:e}=new Uint8Array(i);return c(new DataView(e),i)}}return c(n(u,i),o)};return a})(new Map,e)(0),c="",{toString:a}={},{keys:o}=Object,u=e=>{const r=typeof e;if("object"!==r||!e)return[0,r];const t=a.call(e).slice(8,-1);switch(t){case"Array":return[1,c];case"Object":return[2,c];case"Date":return[3,c];case"RegExp":return[4,c];case"Map":return[5,c];case"Set":return[6,c];case"DataView":return[1,t]}return t.includes("Array")?[1,t]:e instanceof Error?[7,e.name||"Error"]:[2,t]},i=([e,r])=>0===e&&("function"===r||"symbol"===r),f=(e,{json:r,lossy:t}={})=>{const n=[];return((e,r,t,n)=>{const s=(e,r)=>{const s=n.push(e)-1;return t.set(r,s),s},a=n=>{if(t.has(n))return t.get(n);let[f,l]=u(n);switch(f){case 0:{let r=n;switch(l){case"bigint":f=8,r=n.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+l);r=null;break;case"undefined":return s([-1],n)}return s([f,r],n)}case 1:{if(l){let e=n;return"DataView"===l?e=new Uint8Array(n.buffer):"ArrayBuffer"===l&&(e=new Uint8Array(n)),s([l,[...e]],n)}const e=[],r=s([f,e],n);for(const r of n)e.push(a(r));return r}case 2:{if(l)switch(l){case"BigInt":return s([l,n.toString()],n);case"Boolean":case"Number":case"String":return s([l,n.valueOf()],n)}if(r&&"toJSON"in n)return a(n.toJSON());const t=[],c=s([f,t],n);for(const r of o(n))!e&&i(u(n[r]))||t.push([a(r),a(n[r])]);return c}case 3:return s([f,isNaN(n.getTime())?c:n.toISOString()],n);case 4:{const{source:e,flags:r}=n;return s([f,{source:e,flags:r}],n)}case 5:{const r=[],t=s([f,r],n);for(const[t,s]of n)(e||!i(u(t))&&!i(u(s)))&&r.push([a(t),a(s)]);return t}case 6:{const r=[],t=s([f,r],n);for(const t of n)!e&&i(u(t))||r.push(a(t));return t}}const{message:w}=n;return s([f,{name:l,message:w}],n)};return a})(!(r||t),!!r,new Map,n)(e),n},{parse:l,stringify:w}=JSON,g={json:!0,lossy:!0};return e.parse=e=>s(l(e)),e.stringify=e=>w(f(e,g)),e}({}); +var StructuredJSON=function(e){"use strict";const{defineProperty:r}=Object,t="object"==typeof self?self:globalThis,n=(e,r)=>{switch(e){case"Function":case"SharedWorker":case"Worker":case"eval":case"setInterval":case"setTimeout":throw new TypeError("unable to deserialize "+e)}return new t[e](r)},s=e=>((e,s)=>{const a=(r,t)=>(e.set(t,r),r),c=o=>{if(e.has(o))return e.get(o);const[u,i]=s[o];switch(u){case 0:case-1:return a(i,o);case 1:{const e=a([],o);for(const r of i)e.push(c(r));return e}case 2:{const e=a({},o);for(const[t,n]of i){const s=c(t),a=c(n);"__proto__"===s?r(e,s,{value:a,configurable:!0,enumerable:!0,writable:!0}):e[s]=a}return e}case 3:return a(new Date(i),o);case 4:{const{source:e,flags:r}=i;return a(new RegExp(e,r),o)}case 5:{const e=a(new Map,o);for(const[r,t]of i)e.set(c(r),c(t));return e}case 6:{const e=a(new Set,o);for(const r of i)e.add(c(r));return e}case 7:{const{name:e,message:r}=i;return a("function"==typeof t[e]?n(e,r):new Error(r),o)}case 8:return a(BigInt(i),o);case"BigInt":return a(Object(BigInt(i)),o);case"ArrayBuffer":return a(new Uint8Array(i).buffer,i);case"DataView":{const{buffer:e}=new Uint8Array(i);return a(new DataView(e),i)}case"-0":return-0}return a(n(u,i),o)};return c})(new Map,e)(0),a="",{toString:c}={},{keys:o,is:u}=Object,i=e=>{const r=typeof e;if("object"!==r||!e)return[0,r];const t=c.call(e).slice(8,-1);switch(t){case"Array":return[1,a];case"Object":return[2,a];case"Date":return[3,a];case"RegExp":return[4,a];case"Map":return[5,a];case"Set":return[6,a];case"DataView":return[1,t]}return t.includes("Array")?[1,t]:e instanceof Error?[7,e.name||"Error"]:[2,t]},f=([e,r])=>0===e&&("function"===r||"symbol"===r),l=(e,{json:r,lossy:t}={})=>{const n=[];return((e,r,t,n)=>{const s=(e,r)=>{const s=n.push(e)-1;return t.set(r,s),s},c=l=>{if(t.has(l))return t.get(l);let[w,g]=i(l);switch(w){case 0:{let r=l;switch(g){case"bigint":w=8,r=l.toString();break;case"number":if(!l&&u(l,-0))return n.push(["-0"])-1;break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+g);r=null;break;case"undefined":return s([-1],l)}return s([w,r],l)}case 1:{if(g){let e=l;return"DataView"===g?e=new Uint8Array(l.buffer):"ArrayBuffer"===g&&(e=new Uint8Array(l)),s([g,[...e]],l)}const e=[],r=s([w,e],l);for(const r of l)e.push(c(r));return r}case 2:{if(g)switch(g){case"BigInt":return s([g,l.toString()],l);case"Boolean":case"Number":case"String":return s([g,l.valueOf()],l)}if(r&&"toJSON"in l)return c(l.toJSON());const t=[],n=s([w,t],l);for(const r of o(l))!e&&f(i(l[r]))||t.push([c(r),c(l[r])]);return n}case 3:return s([w,isNaN(l.getTime())?a:l.toISOString()],l);case 4:{const{source:e,flags:r}=l;return s([w,{source:e,flags:r}],l)}case 5:{const r=[],t=s([w,r],l);for(const[t,n]of l)(e||!f(i(t))&&!f(i(n)))&&r.push([c(t),c(n)]);return t}case 6:{const r=[],t=s([w,r],l);for(const t of l)!e&&f(i(t))||r.push(c(t));return t}}const{message:b}=l;return s([w,{name:g,message:b}],l)};return c})(!(r||t),!!r,new Map,n)(e),n},{parse:w,stringify:g}=JSON,b={json:!0,lossy:!0};return e.parse=e=>s(w(e)),e.stringify=e=>g(l(e,b)),e}({}); diff --git a/test/index.js b/test/index.js index d869d0e..b03edc4 100644 --- a/test/index.js +++ b/test/index.js @@ -193,7 +193,9 @@ for (const Class of [Error, EvalError, RangeError, ReferenceError, SyntaxError, custom.name = ''; assert(deserialize(serialize(custom)).name, 'Error', 'empty name results into Error'); - assert(deserialize(serialize(JSON.parse('{"__proto__": {"foo": "bar"}}'))).foo, undefined); + assert(deserialize(serialize(JSON.parse('{"__proto__": {"foo": "bar"}}'))).foo, undefined, '__proto__'); + + assert(deserialize(serialize(-0)), -0, '-0'); } require('./eval.js');