diff --git a/cjs/deserialize.js b/cjs/deserialize.js index cb84880..0508a85 100644 --- a/cjs/deserialize.js +++ b/cjs/deserialize.js @@ -86,10 +86,10 @@ const deserializer = ($, _) => { case 'BigInt': return as(Object(BigInt(value)), index); case 'ArrayBuffer': - return as(new Uint8Array(value).buffer, value); + return as(new Uint8Array(value).buffer, index); case 'DataView': { const { buffer } = new Uint8Array(value); - return as(new DataView(buffer), value); + return as(new DataView(buffer), index); } case '-0': return -0; } diff --git a/esm/deserialize.js b/esm/deserialize.js index 86920f0..8888d45 100644 --- a/esm/deserialize.js +++ b/esm/deserialize.js @@ -88,10 +88,10 @@ const deserializer = ($, _) => { case 'BigInt': return as(Object(BigInt(value)), index); case 'ArrayBuffer': - return as(new Uint8Array(value).buffer, value); + return as(new Uint8Array(value).buffer, index); case 'DataView': { const { buffer } = new Uint8Array(value); - return as(new DataView(buffer), value); + return as(new DataView(buffer), index); } case '-0': return -0; } diff --git a/structured-json.js b/structured-json.js index 4fa6359..999e527 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 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}({}); +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,o);case"DataView":{const{buffer:e}=new Uint8Array(i);return a(new DataView(e),o)}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 b03edc4..3cbfb4e 100644 --- a/test/index.js +++ b/test/index.js @@ -8,6 +8,25 @@ const assert = (expected, result, message = '') => { } }; +{ + const buffer = new Uint8Array([10, 20, 30]).buffer; + for (const value of [buffer, new DataView(buffer)]) { + const original = { + first: value, + second: value, + map: new Map([[value, value]]) + }; + for (const result of [deserialize(serialize(original)), parse(stringify(original))]) { + assert(result.first === value, false, 'clones the original value'); + assert(result.first, result.second, 'preserves repeated buffer or view references'); + assert(result.map.get(result.first), result.first, 'preserves Map key and value references'); + const bytes = result.first instanceof DataView ? + new Uint8Array(result.first.buffer) : new Uint8Array(result.first); + assert([...bytes].join(','), '10,20,30'); + } + } +} + // @see https://github.com/ungap/structured-clone/issues/4 const withUndefined = stringify({foo: 'test', bar: undefined, foobar: null}); assert(withUndefined, '[[2,[[1,2],[3,4],[5,6]]],[0,"foo"],[0,"test"],[0,"bar"],[-1],[0,"foobar"],[0,null]]');