Offroaders123 / NBTify

A library to read and write NBT files on the web!
http://npm.im/nbtify
MIT License
42 stars 5 forks source link

Circular Data Structure Handling #22

Open Offroaders123 opened 1 year ago

Offroaders123 commented 1 year ago

Thinking about the behavior of JSON.stringify(), I remembered that it throws an error when encountering circular object references. I realized that I haven't specifically handled that in NBTify, so you simply get to a Maximum call stack exceeded error when writing a circular object structure to NBT. This isn't ideal, and I should detect this before the user runs into it, just like JSON.stringify() does.

Detecting and fixing circular references in JavaScript - Stack Overflow TypeError: cyclic object value - MDN

Found an example of how to detect them on MDN, this one can be used as a JSON replacer callback, and it will remove any circular object references before serializing to JSON. While this essentially makes the circular object "fully" serializable, I will go with throwing an error instead, like JSON.stringify() does, since I want it to be something that should be handled specifically by the user, where they can choose what to do with the circular references, rather than implicitly removing them inside of NBTify.

const getCircularReplacer = () => {
  const seen = new WeakSet();
  return (key, value) => {
    if (typeof value === "object" && value !== null) {
      if (seen.has(value)) {
        return;
      }
      seen.add(value);
    }
    return value;
  };
};

JSON.stringify(circularReference, getCircularReplacer());
// {"otherData":123}

Because of that, it also further sways me to throw an error for inconsistent ListTag item types, rather than silently excluding them from the written NBT buffer (#21).

I think I will silently remove unsupported JavaScript types during the writing process though, like for null, undefined, Symbol, and function values (#20). That's what JSON.stringify() does for any function values on objects, and I think it keeps things straightforward to be able to simply pass most objects right into NBTify, but also still be explicit in throwing knowledgeable errors for object structure issues, like circular object references and array item type inconsistencies.