async function evaluateInSession(session, fn, deps) {
  const depsSerialized = deps ? deps.join('\n') : '';

  const expression = typeof fn === 'string' ?
    fn :
    `(() => {
      ${depsSerialized}
      return (${fn.toString()})();
    })()`;
  const {result, exceptionDetails} = await session.send('Runtime.evaluate', {
    awaitPromise: true,
    returnByValue: true,
    expression,
  });

  if (exceptionDetails) {
    const text = exceptionDetails.exception?.description || exceptionDetails.text;
    const name = typeof fn === 'string' ? '<stringified>' : fn.name;
    throw new Error(`Evaluation exception: ${text}\n    at ${name} (Runtime.evaluate)`);
  }

  return result.value;
}
Ideally we would use `page.evaluate` instead of this,
but we can't get a Puppeteer page object for the DevTools frontend.
This is a light re-implementation of `page.evaluate`.

@template R [unknown]
@param {puppeteer.CDPSession} session
@param {string|(() => (R|Promise<R>))} fn
@param {Function[]} [deps]
@return {Promise<R>}