const {parse: $parse, stringify: $stringify} = JSON;
const {keys} = Object;
const Primitive = String;
const primitive = "string";
const ignore = {};
const object = "object";
const noop = (_, value) => value;
const primitives = (value) => value instanceof Primitive ? Primitive(value) : value;
const Primitives = (_, value) => typeof value === primitive ? new Primitive(value) : value;
const resolver = (input, lazy, parsed, $) => (output) => {
  for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
    const k = ke[y];
    const value = output[k];
    if (value instanceof Primitive) {
      const tmp = input[+value];
      if (typeof tmp === object && !parsed.has(tmp)) {
        parsed.add(tmp);
        output[k] = ignore;
        lazy.push({o: output, k, r: tmp});
      } else
        output[k] = $.call(output, k, tmp);
    } else if (output[k] !== ignore)
      output[k] = $.call(output, k, value);
  }
  return output;
};
const set = (known, input, value) => {
  const index = Primitive(input.push(value) - 1);
  known.set(value, index);
  return index;
};
const parse = (text, reviver) => {
  const input = $parse(text, Primitives).map(primitives);
  const $ = reviver || noop;
  let value = input[0];
  if (typeof value === object && value) {
    const lazy = [];
    const revive = resolver(input, lazy, new Set(), $);
    value = revive(value);
    let i = 0;
    while (i < lazy.length) {
      const {o, k, r} = lazy[i++];
      o[k] = $.call(o, k, revive(r));
    }
  }
  return $.call({"": value}, "", value);
};
const stringify = (value, replacer, space) => {
  const $ = replacer && typeof replacer === object ? (k, v) => k === "" || -1 < replacer.indexOf(k) ? v : void 0 : replacer || noop;
  const known = new Map();
  const input = [];
  const output = [];
  let i = +set(known, input, $.call({"": value}, "", value));
  let firstRun = !i;
  while (i < input.length) {
    firstRun = true;
    output[i] = $stringify(input[i++], replace, space);
  }
  return "[" + output.join(",") + "]";
  function replace(key, value2) {
    if (firstRun) {
      firstRun = !firstRun;
      return value2;
    }
    const after = $.call(this, key, value2);
    switch (typeof after) {
      case object:
        if (after === null)
          return after;
      case primitive:
        return known.get(after) || set(known, input, after);
    }
    return after;
  }
};
const toJSON = (value) => $parse(stringify(value));
const fromJSON = (value) => parse($stringify(value));
export {fromJSON, parse, stringify, toJSON};
export default null;
