const REMAP_KEY = "$$jsone$remap$$";
const hasOwn = Object.prototype.hasOwnProperty;
export function encode(value) {
return encodeValue(value);
}
function encodeValue(value, seen) {
if (typeof value === "bigint") return { [REMAP_KEY]: value.toString() };
else if (typeof value === "number") {
if (Number.isNaN(value)) return { [REMAP_KEY]: 1 };
else if (value === Number.POSITIVE_INFINITY) return { [REMAP_KEY]: 2 };
else if (value === Number.NEGATIVE_INFINITY) return { [REMAP_KEY]: 3 };
else if (
Number.isInteger(value) &&
(value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER)
)
return { [REMAP_KEY]: value.toString() };
}
if (typeof value !== "object" || value === null) return value;
seen ??= new WeakSet();
if (seen.has(value)) return value;
seen.add(value);
if (Array.isArray(value)) {
for (let index = 0; index < value.length; index += 1)
value[index] = encodeValue(value[index], seen);
return value;
}
for (const key in value)
if (hasOwn.call(value, key)) value[key] = encodeValue(value[key], seen);
return value;
}
export function decode(value) {
return decodeValue(value);
}
function decodeValue(value, seen) {
if (typeof value !== "object" || value === null) return value;
let isRemappedWrapper = false;
for (const key in value) {
if (!hasOwn.call(value, key)) continue;
if (key !== REMAP_KEY) {
isRemappedWrapper = false;
break;
}
isRemappedWrapper = true;
}
if (isRemappedWrapper) {
const remapped = value[REMAP_KEY];
if (typeof remapped === "string") return BigInt(remapped);
if (remapped === 1) return Number.NaN;
if (remapped === 2) return Number.POSITIVE_INFINITY;
if (remapped === 3) return Number.NEGATIVE_INFINITY;
throw new TypeError(
`jsone decode error: found unknown remap key ${remapped}.`,
);
}
seen ??= new WeakSet();
if (seen.has(value)) return value;
seen.add(value);
if (Array.isArray(value)) {
for (let index = 0; index < value.length; index += 1)
value[index] = decodeValue(value[index], seen);
return value;
}
for (const key in value)
if (hasOwn.call(value, key)) value[key] = decodeValue(value[key], seen);
return value;
}