Spaces:
Runtime error
Runtime error
import { | |
HOLE, | |
NAN, | |
NEGATIVE_INFINITY, | |
NEGATIVE_ZERO, | |
POSITIVE_INFINITY, | |
UNDEFINED | |
} from './constants.js'; | |
/** | |
* Revive a value serialized with `devalue.stringify` | |
* @param {string} serialized | |
* @param {Record<string, (value: any) => any>} [revivers] | |
*/ | |
export function parse(serialized, revivers) { | |
return unflatten(JSON.parse(serialized), revivers); | |
} | |
/** | |
* Revive a value flattened with `devalue.stringify` | |
* @param {number | any[]} parsed | |
* @param {Record<string, (value: any) => any>} [revivers] | |
*/ | |
export function unflatten(parsed, revivers) { | |
if (typeof parsed === 'number') return hydrate(parsed, true); | |
if (!Array.isArray(parsed) || parsed.length === 0) { | |
throw new Error('Invalid input'); | |
} | |
const values = /** @type {any[]} */ (parsed); | |
const hydrated = Array(values.length); | |
/** | |
* @param {number} index | |
* @returns {any} | |
*/ | |
function hydrate(index, standalone = false) { | |
if (index === UNDEFINED) return undefined; | |
if (index === NAN) return NaN; | |
if (index === POSITIVE_INFINITY) return Infinity; | |
if (index === NEGATIVE_INFINITY) return -Infinity; | |
if (index === NEGATIVE_ZERO) return -0; | |
if (standalone) throw new Error(`Invalid input`); | |
if (index in hydrated) return hydrated[index]; | |
const value = values[index]; | |
if (!value || typeof value !== 'object') { | |
hydrated[index] = value; | |
} else if (Array.isArray(value)) { | |
if (typeof value[0] === 'string') { | |
const type = value[0]; | |
const reviver = revivers?.[type]; | |
if (reviver) { | |
return (hydrated[index] = reviver(hydrate(value[1]))); | |
} | |
switch (type) { | |
case 'Date': | |
hydrated[index] = new Date(value[1]); | |
break; | |
case 'Set': | |
const set = new Set(); | |
hydrated[index] = set; | |
for (let i = 1; i < value.length; i += 1) { | |
set.add(hydrate(value[i])); | |
} | |
break; | |
case 'Map': | |
const map = new Map(); | |
hydrated[index] = map; | |
for (let i = 1; i < value.length; i += 2) { | |
map.set(hydrate(value[i]), hydrate(value[i + 1])); | |
} | |
break; | |
case 'RegExp': | |
hydrated[index] = new RegExp(value[1], value[2]); | |
break; | |
case 'Object': | |
hydrated[index] = Object(value[1]); | |
break; | |
case 'BigInt': | |
hydrated[index] = BigInt(value[1]); | |
break; | |
case 'null': | |
const obj = Object.create(null); | |
hydrated[index] = obj; | |
for (let i = 1; i < value.length; i += 2) { | |
obj[value[i]] = hydrate(value[i + 1]); | |
} | |
break; | |
default: | |
throw new Error(`Unknown type ${type}`); | |
} | |
} else { | |
const array = new Array(value.length); | |
hydrated[index] = array; | |
for (let i = 0; i < value.length; i += 1) { | |
const n = value[i]; | |
if (n === HOLE) continue; | |
array[i] = hydrate(n); | |
} | |
} | |
} else { | |
/** @type {Record<string, any>} */ | |
const object = {}; | |
hydrated[index] = object; | |
for (const key in value) { | |
const n = value[key]; | |
object[key] = hydrate(n); | |
} | |
} | |
return hydrated[index]; | |
} | |
return hydrate(0); | |
} | |