docs: function createTransportClientType() {
      log.info("create websocket");
      // [ -Events- ]
      // open - when the transport is open
      // close - when transport is closed
      // message - when transport receives a JSON message
      // [ -Constructor- ]
      function TransportClient() {
          log.info("instantiate websocket");
          // get non-ambiguous this
          var self = this;
          // Call parent constructor
          TransportClient.super_.call(self);
          // Internal structures
          self._ws = undefined;
          self._id = _.uniqueId(); // debugging tool only
          self._state = "NOT_OPENED";
          // state listeners
          self.once("open", function handleOpened() {
              self._state = "OPEN";
          }).once("close", function handleClosed() {
              self._state = "CLOSED";
          });
          // constructor - return undefined
          return undefined;
      }
      // [ -Inheritance- ]
      util.inherits(TransportClient, events.EventEmitter);
      // [ -Private API- ]
      // handle a received message
      // emits 'message' with a JSON argument
      TransportClient.prototype._handleMessage = function transportClientHandleMessage(message, flags) {
          log.info("websocket on message");
          // binay messages are not supported
          if (flags.binary) {
              var error = new VError("Got binary message from remote hub (not supported).");
              error.hubMessage = message;
              // emit error because this occurred asynchronously, and not necessarily due to user or local
              // programmer error
              this.emit('error', error);
          } else {
              // try to structure the message
              var structured;
              try {
                  structured = JSON.parse(message);
              } catch (e) {
                  var error = new VError(e, "cannot parse message from remote hub as JSON");
                  error.hubMessage = message;
                  // emit error because this occurred asynchronously, and not necessarily due to user or local
                  // programmer error
                  this.emit('error', error);
                  // need to return here, because js has no try/catch/else,
                  // and we don't want to continue after this error.
                  return null;
              }
              // else - a non-binary well-formatted JSON message
              this.emit("message", structured);
          }
          // allow chaining
          return this;
      };
      // proxy events from the websocket
      TransportClient.prototype._proxyEvents = function transportClientProxyEvents() {
          this._ws.on('open', this.emit.bind(this, 'open'));
          this._ws.on('close', this.emit.bind(this, 'close'));
          this._ws.on('error', this.emit.bind(this, 'error'));
          // handle messages from remote hub
          this._ws.on("message", this._handleMessage.bind(this));
          // allow chaining
          return this;
      };
      // [ -Public API- ]
      // connect to a remote websocket hub
      // callback is called when the client is connected to the hub
      TransportClient.prototype.connect = function transportClientConnect(url, cb) {
          log.info("websocket client connect");
          // only allow this to be called once on the object
          if (this._ws) {
              // throw immediately - this is programmer error
              throw new VError("'connect' has already been called on this transport object.");
          }
          // add cb to listeners
          if (cb) {
              this.on('open', cb);
          }
          // open a websocket connection
          this._ws = new ws(url);
          // proxy some events
          this._proxyEvents();
          // it doesn't make sense to do anything with the object until connect
          // calls back, so return null
          return null;
      };
      // wrap a websocket conn in a Transport Client
      TransportClient.prototype.wrap = function transportClientWrap(websocketConnection) {
          log.info("websocket client wrap");
          var wsc = websocketConnection;
          // only allow this to be called once on the object
          if (this._ws) {
              // throw immediately - this is programmer error
              throw new VError("'connect' has already been called on this transport object.");
          }
          // theoretically the connection is already open
          this._ws = wsc;
          if (this._ws.readyState === ws.OPEN) {
              this._state = "OPEN";
          } else {
              var error = new VError("must only wrap websockets which are OPEN");
              error.state = ws.readyState;
              throw error;
          }
          // proxy some events
          this._proxyEvents();
          // allow chaining
          return this;
      };
      // close connection
      TransportClient.prototype.close = function transportClientClose() {
          log.info("websocket close");
          if (this._state !== "OPEN") {
              throw new VError("Cannot close a client which is not open");
          }
          this._ws.close();
          // it doesn't make sense to do anything with the object
          // after close, so return null
          return null;
      };
      // send to remote websocket
      TransportClient.prototype.send = function transportClientSend(message, cb) {
          log.info("websocket send", message);
          if (this._state !== "OPEN") {
              throw new VError("Nonsensical to call before 'open' or after 'close' events");
          }
          var stringified;
          try {
              stringified = JSON.stringify(message);
          } catch (e) {
              // do not use the cb to emit - this is a programmer error - throw immediately
              var error = new VError(e, "could not stringify JSON message");
              error.jsonMessage = message;
              throw error;
          }
          log.debug("stringified: ", stringified);
          // explicitly check state, because the ws module has bad
          // internal behavior if the socket is closing when you try
          // to send a message. see https://github.com/websockets/ws/issues/366
          if (this._ws.readyState === ws.OPEN) {
              this._ws.send(stringified, cb);
          } else {
              // throw - this is a programming error (sending while ws in bad state)
              throw new VError("The client connection is not open.  Cannot send.");
          }
          // allow chaining
          return this;
      };
      return TransportClient;
  }