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 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 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} */ const object = {}; hydrated[index] = object; for (const key in value) { const n = value[key]; object[key] = hydrate(n); } } return hydrated[index]; } return hydrate(0); }