THRIFT-5855: Add nodejs fuzzers

Add fuzzers for nodejs support, to improve the reliability/robustness of the implementation
diff --git a/lib/nodejs/test/fuzz/fuzz_common.js b/lib/nodejs/test/fuzz/fuzz_common.js
new file mode 100644
index 0000000..1eb5f79
--- /dev/null
+++ b/lib/nodejs/test/fuzz/fuzz_common.js
@@ -0,0 +1,107 @@
+const thrift = require("../../lib/thrift");
+const FuzzTest = require("./gen-nodejs/FuzzTestNoUuid_types");
+// const { FuzzedDataProvider } = require("@jazzer.js/core");
+
+/**
+ * Creates a parser fuzzer function for a specific protocol
+ * @param {Function} protocolFactory - The Thrift protocol factory function
+ * @param {boolean} [readMessageBegin=false] - Whether to call readMessageBegin before reading the test instance
+ * This is needed for protocols that do not support parsing just a struct, such as TJSONProtocol.
+ * @returns {Function} A fuzzer function that can be used with Jazzer.js
+ */
+function createParserFuzzer(protocolFactory, readMessageBegin = false) {
+  return function fuzz(data) {
+    if (data.length < 2) {
+      return;
+    }
+
+    try {
+      // Set up transport with input data
+      const transport = new thrift.TFramedTransport(data);
+      const protocol = protocolFactory(transport);
+      const testInstance = new FuzzTest.FuzzTest();
+      if (readMessageBegin) {
+        protocol.readMessageBegin();
+      }
+      testInstance[Symbol.for("read")](protocol);
+    } catch (e) {
+      if (
+        !(
+          e.name === "InputBufferUnderrunError" ||
+          e.name === "TProtocolException"
+        )
+      ) {
+        // TODO: Are other exceptions expected?
+        // console.log(e);
+      }
+    }
+  };
+}
+
+/**
+ * Creates a roundtrip fuzzer function for a specific protocol
+ * @param {Function} protocolFactory - The Thrift protocol factory function
+ * @param {boolean} [readMessageBegin=false] - Whether to call readMessageBegin before reading the test instance
+ * This is needed for protocols that do not support parsing just a struct, such as TJSONProtocol.
+ * @returns {Function} A fuzzer function that can be used with Jazzer.js
+ */
+function createRoundtripFuzzer(protocolFactory, readMessageBegin = false) {
+  return function fuzz(data) {
+    if (data.length < 2) {
+      return;
+    }
+
+    try {
+      // First deserialize using framed transport for input
+      const transport1 = new thrift.TFramedTransport(data);
+      const protocol1 = protocolFactory(transport1);
+      const testInstance = new FuzzTest.FuzzTest();
+      if (readMessageBegin) {
+        protocol1.readMessageBegin();
+      }
+      testInstance[Symbol.for("read")](protocol1);
+
+      // Then serialize using buffered transport with callback
+      let serializedData;
+      const transport2 = new thrift.TBufferedTransport(null, function (buf) {
+        serializedData = buf;
+      });
+      const protocol2 = protocolFactory(transport2);
+      testInstance[Symbol.for("write")](protocol2);
+      protocol2.flush();
+
+      if (!serializedData) {
+        throw new Error("Serialization failed - no data produced");
+      }
+
+      // Finally deserialize again and compare using framed transport
+      const transport3 = new thrift.TFramedTransport(serializedData);
+      const protocol3 = protocolFactory(transport3);
+      const deserialized = new FuzzTest.FuzzTest();
+      if (readMessageBegin) {
+        protocol3.readMessageBegin();
+      }
+      deserialized[Symbol.for("read")](protocol3);
+
+      // Compare the objects
+      if (!testInstance.equals(deserialized)) {
+        throw new Error("Roundtrip comparison failed");
+      }
+    } catch (e) {
+      if (
+        !(
+          e.name === "InputBufferUnderrunError" ||
+          e.name === "TProtocolException"
+        )
+      ) {
+        // TODO: Are other exceptions expected?
+        // console.log(e);
+      }
+    }
+  };
+}
+
+module.exports = {
+  createParserFuzzer,
+  createRoundtripFuzzer,
+};