constructor(serverUrl, roomname, doc, {
    connect = true,
    awareness = new awarenessProtocol.Awareness(doc),
    params = {},
    WebSocketPolyfill = WebSocket,
    resyncInterval = -1
  } = {}) {
    super(); // ensure that url is always ends with /

    while (serverUrl[serverUrl.length - 1] === '/') {
      serverUrl = serverUrl.slice(0, serverUrl.length - 1);
    }

    const encodedParams = url.encodeQueryParams(params);
    this.bcChannel = serverUrl + '/' + roomname;
    this.url = serverUrl + '/' + roomname + (encodedParams.length === 0 ? '' : '?' + encodedParams);
    this.roomname = roomname;
    this.doc = doc;
    this._WS = WebSocketPolyfill;
    this.awareness = awareness;
    this.wsconnected = false;
    this.wsconnecting = false;
    this.bcconnected = false;
    this.wsUnsuccessfulReconnects = 0;
    this.messageHandlers = messageHandlers.slice();
    this.mux = mutex.createMutex();
    /**
     * @type {boolean}
     */

    this._synced = false;
    /**
     * @type {WebSocket?}
     */

    this.ws = null;
    this.wsLastMessageReceived = 0;
    /**
     * Whether to connect to other peers or not
     * @type {boolean}
     */

    this.shouldConnect = connect;
    /**
     * @type {number}
     */

    this._resyncInterval = 0;

    if (resyncInterval > 0) {
      this._resyncInterval =
      /** @type {any} */
      setInterval(() => {
        if (this.ws) {
          // resend sync step 1
          const encoder = encoding.createEncoder();
          encoding.writeVarUint(encoder, messageSync);
          syncProtocol.writeSyncStep1(encoder, doc);
          this.ws.send(encoding.toUint8Array(encoder));
        }
      }, resyncInterval);
    }
    /**
     * @param {ArrayBuffer} data
     */


    this._bcSubscriber = data => {
      this.mux(() => {
        const encoder = readMessage(this, new Uint8Array(data), false);

        if (encoding.length(encoder) > 1) {
          bc.publish(this.bcChannel, encoding.toUint8Array(encoder));
        }
      });
    };
    /**
     * Listens to Yjs updates and sends them to remote peers (ws and broadcastchannel)
     * @param {Uint8Array} update
     * @param {any} origin
     */


    this._updateHandler = (update, origin) => {
      if (origin !== this) {
        const encoder = encoding.createEncoder();
        encoding.writeVarUint(encoder, messageSync);
        syncProtocol.writeUpdate(encoder, update);
        broadcastMessage(this, encoding.toUint8Array(encoder));
      }
    };

    this.doc.on('update', this._updateHandler);
    /**
     * @param {any} changed
     * @param {any} origin
     */

    this._awarenessUpdateHandler = ({
      added,
      updated,
      removed
    }, origin) => {
      const changedClients = added.concat(updated).concat(removed);
      const encoder = encoding.createEncoder();
      encoding.writeVarUint(encoder, messageAwareness);
      encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(awareness, changedClients));
      broadcastMessage(this, encoding.toUint8Array(encoder));
    };

    if (typeof window !== 'undefined') {
      window.addEventListener('beforeunload', () => {
        awarenessProtocol.removeAwarenessStates(this.awareness, [doc.clientID], 'window unload');
      });
    }

    awareness.on('update', this._awarenessUpdateHandler);
    this._checkInterval =
    /** @type {any} */
    setInterval(() => {
      if (this.wsconnected && messageReconnectTimeout < time.getUnixTime() - this.wsLastMessageReceived) {
        // no message received in a long time - not even your own awareness
        // updates (which are updated every 15 seconds)

        /** @type {WebSocket} */
        this.ws.close();
      }
    }, messageReconnectTimeout / 10);

    if (connect) {
      this.connect();
    }
  }
@param {string} serverUrl
@param {string} roomname
@param {Y.Doc} doc
@param {object} [opts]
@param {boolean} [opts.connect]
@param {awarenessProtocol.Awareness} [opts.awareness]
@param {Object<string,string>} [opts.params]
@param {typeof WebSocket} [opts.WebSocketPolyfill] Optionall provide a WebSocket polyfill
@param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds