text: import { Transport } from "../transport.js";
  import parseqs from "parseqs";
  import yeast from "yeast";
  import { pick } from "../util.js";
  import { defaultBinaryType, nextTick, usingBrowserWebSocket, WebSocket } from "./websocket-constructor.js";
  import debugModule from "debug"; // debug()
  import { encodePacket } from "engine.io-parser";
  const debug = debugModule("engine.io-client:websocket"); // debug()
  // detect ReactNative environment
  const isReactNative = typeof navigator !== "undefined" &&
      typeof navigator.product === "string" &&
      navigator.product.toLowerCase() === "reactnative";
  export class WS extends Transport {
      /**
       * WebSocket transport constructor.
       *
       * @api {Object} connection options
       * @api public
       */
      constructor(opts) {
          super(opts);
          this.supportsBinary = !opts.forceBase64;
      }
      /**
       * Transport name.
       *
       * @api public
       */
      get name() {
          return "websocket";
      }
      /**
       * Opens socket.
       *
       * @api private
       */
      doOpen() {
          if (!this.check()) {
              // let probe timeout
              return;
          }
          const uri = this.uri();
          const protocols = this.opts.protocols;
          // React Native only supports the 'headers' option, and will print a warning if anything else is passed
          const opts = isReactNative
              ? {}
              : pick(this.opts, "agent", "perMessageDeflate", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "rejectUnauthorized", "localAddress", "protocolVersion", "origin", "maxPayload", "family", "checkServerIdentity");
          if (this.opts.extraHeaders) {
              opts.headers = this.opts.extraHeaders;
          }
          try {
              this.ws =
                  usingBrowserWebSocket && !isReactNative
                      ? protocols
                          ? new WebSocket(uri, protocols)
                          : new WebSocket(uri)
                      : new WebSocket(uri, protocols, opts);
          }
          catch (err) {
              return this.emit("error", err);
          }
          this.ws.binaryType = this.socket.binaryType || defaultBinaryType;
          this.addEventListeners();
      }
      /**
       * Adds event listeners to the socket
       *
       * @api private
       */
      addEventListeners() {
          this.ws.onopen = () => {
              if (this.opts.autoUnref) {
                  this.ws._socket.unref();
              }
              this.onOpen();
          };
          this.ws.onclose = this.onClose.bind(this);
          this.ws.onmessage = ev => this.onData(ev.data);
          this.ws.onerror = e => this.onError("websocket error", e);
      }
      /**
       * Writes data to socket.
       *
       * @param {Array} array of packets.
       * @api private
       */
      write(packets) {
          this.writable = false;
          // encodePacket efficient as it uses WS framing
          // no need for encodePayload
          for (let i = 0; i < packets.length; i++) {
              const packet = packets[i];
              const lastPacket = i === packets.length - 1;
              encodePacket(packet, this.supportsBinary, data => {
                  // always create a new object (GH-437)
                  const opts = {};
                  if (!usingBrowserWebSocket) {
                      if (packet.options) {
                          opts.compress = packet.options.compress;
                      }
                      if (this.opts.perMessageDeflate) {
                          const len = "string" === typeof data ? Buffer.byteLength(data) : data.length;
                          if (len < this.opts.perMessageDeflate.threshold) {
                              opts.compress = false;
                          }
                      }
                  }
                  // Sometimes the websocket has already been closed but the browser didn't
                  // have a chance of informing us about it yet, in that case send will
                  // throw an error
                  try {
                      if (usingBrowserWebSocket) {
                          // TypeError is thrown when passing the second argument on Safari
                          this.ws.send(data);
                      }
                      else {
                          this.ws.send(data, opts);
                      }
                  }
                  catch (e) {
                      debug("websocket closed before onclose event");
                  }
                  if (lastPacket) {
                      // fake drain
                      // defer to next tick to allow Socket to clear writeBuffer
                      nextTick(() => {
                          this.writable = true;
                          this.emit("drain");
                      }, this.setTimeoutFn);
                  }
              });
          }
      }
      /**
       * Closes socket.
       *
       * @api private
       */
      doClose() {
          if (typeof this.ws !== "undefined") {
              this.ws.close();
              this.ws = null;
          }
      }
      /**
       * Generates uri for connection.
       *
       * @api private
       */
      uri() {
          let query = this.query || {};
          const schema = this.opts.secure ? "wss" : "ws";
          let port = "";
          // avoid port if default for schema
          if (this.opts.port &&
              (("wss" === schema && Number(this.opts.port) !== 443) ||
                  ("ws" === schema && Number(this.opts.port) !== 80))) {
              port = ":" + this.opts.port;
          }
          // append timestamp to URI
          if (this.opts.timestampRequests) {
              query[this.opts.timestampParam] = yeast();
          }
          // communicate binary support capabilities
          if (!this.supportsBinary) {
              query.b64 = 1;
          }
          const encodedQuery = parseqs.encode(query);
          const ipv6 = this.opts.hostname.indexOf(":") !== -1;
          return (schema +
              "://" +
              (ipv6 ? "[" + this.opts.hostname + "]" : this.opts.hostname) +
              port +
              this.opts.path +
              (encodedQuery.length ? "?" + encodedQuery : ""));
      }
      /**
       * Feature detection for WebSocket.
       *
       * @return {Boolean} whether this transport is available.
       * @api public
       */
      check() {
          return (!!WebSocket &&
              !("__initialize" in WebSocket && this.name === WS.prototype.name));
      }
  }
  