text: // chrome-native-messaging module
  //
  // Defines three Transform streams:
  //
  // - Input - transform native messages to JavaScript objects
  // - Output - transform JavaScript objects to native messages
  // - Transform - transform message objects to reply objects
  // - Debug - transform JavaScript objects to lines of JSON (for debugging, obviously)
  
  const stream = require('stream');
  const util = require('util');
  
  function Input() {
    stream.Transform.call(this);
  
    console.log('what does this do?');
    // Transform bytes...
    this._writableState.objectMode = false;
    // ...into objects.
    this._readableState.objectMode = true;
  
    // Unparsed data.
    this.buf = new Buffer(0);
    // Parsed length.
    this.len = null;
  }
  
  util.inherits(Input, stream.Transform);
  
  Input.prototype._transform = function (chunk, encoding, done) {
    // Save this chunk.
    this.buf = Buffer.concat([this.buf, chunk]);
    console.log('WHEN INPUTCALLED');
  
    const self = this;
  
    function parseBuf() {
      // Do we have a length yet?
      if (typeof self.len !== 'number') {
        // Nope. Do we have enough bytes for the length?
        if (self.buf.length >= 4) {
          // Yep. Parse the bytes.
          self.len = self.buf.readUInt32LE(0);
          // Remove the length bytes from the buffer.
          self.buf = self.buf.slice(4);
        }
      }
  
      // Do we have a length yet? (We may have just parsed it.)
      if (typeof self.len === 'number') {
        // Yep. Do we have enough bytes for the message?
        if (self.buf.length >= self.len) {
          // Yep. Slice off the bytes we need.
          const message = self.buf.slice(0, self.len);
          // Remove the bytes for the message from the buffer.
          self.buf = self.buf.slice(self.len);
          // Clear the length so we know we need to parse it again.
          self.len = null;
          // Parse the message bytes.
          const obj = JSON.parse(message.toString());
          // Enqueue it for reading.
          self.push(obj);
          // We could have more messages in the buffer so check again.
          parseBuf();
        }
      }
    }
  
    // Check for a parsable buffer (both length and message).
    parseBuf();
  
    // We're done.
    done();
  };
  
  function Output() {
    stream.Transform.call(this);
  
    this._writableState.objectMode = true;
    this._readableState.objectMode = false;
  }
  
  util.inherits(Output, stream.Transform);
  
  Output.prototype._transform = function (chunk, encoding, done) {
    const len = new Buffer(4);
    const buf = new Buffer(JSON.stringify(chunk));
  
    len.writeUInt32LE(buf.length, 0);
  
    this.push(len);
    this.push(buf);
  
    done();
  };
  
  function Transform(handler) {
    stream.Transform.call(this);
  
    this._writableState.objectMode = true;
    this._readableState.objectMode = true;
  
    this.handler = handler;
  }
  
  util.inherits(Transform, stream.Transform);
  
  Transform.prototype._transform = function (msg, encoding, done) {
    this.handler(msg, this.push.bind(this), done);
  };
  
  function Debug() {
    stream.Transform.call(this);
  
    this._writableState.objectMode = true;
    this._readableState.objectMode = false;
  }
  
  util.inherits(Debug, stream.Transform);
  
  Debug.prototype._transform = function (chunk, encoding, done) {
    this.push(`${JSON.stringify(chunk)}\n`);
  
    done();
  };
  
  exports.Input = Input;
  exports.Output = Output;
  exports.Transform = Transform;
  exports.Debug = Debug;
  