sendCommand(method, ...params) {
    const timeoutMs = this.getNextProtocolTimeout();
    this._nextProtocolTimeout = undefined;

    /** @type {NodeJS.Timeout|undefined} */
    let timeout;
    const timeoutPromise = new Promise((resolve, reject) => {
      // Unexpected setTimeout invocation to preserve the error stack. https://github.com/GoogleChrome/lighthouse/issues/13332
      // eslint-disable-next-line max-len
      timeout = setTimeout(reject, timeoutMs, new LighthouseError(LighthouseError.errors.PROTOCOL_TIMEOUT, {
        protocolMethod: method,
      }));
    });

    const resultPromise = this._cdpSession.send(method, ...params, {
      // Add 50ms to the Puppeteer timeout to ensure the Lighthouse timeout finishes first.
      timeout: timeoutMs + PPTR_BUFFER,
    }).catch((error) => {
      log.formatProtocol('method <= browser ERR', {method}, 'error');
      throw LighthouseError.fromProtocolMessage(method, error);
    });
    const resultWithTimeoutPromise =
      Promise.race([resultPromise, timeoutPromise, this._targetCrashedPromise]);

    return resultWithTimeoutPromise.finally(() => {
      if (timeout) clearTimeout(timeout);
    });
  }
@template {keyof LH.CrdpCommands} C
@param {C} method
@param {LH.CrdpCommands[C]['paramsType']} params
@return {Promise<LH.CrdpCommands[C]['returnType']>}