{"version":3,"sources":["../../src/cypress-api/index.ts","../../src/write-archive/index.ts","../../src/utils/logger.ts","../../src/write-archive/archive-file.ts","../../src/write-archive/dom-snapshot.ts","../../src/resource-archive/index.ts"],"names":["CDP","Version","outputFile","ensureDir","outputJson","join","logger","process","env","LOG","console","log","_args","warn","mime","path","createHash","ArchiveFile","constructor","url","response","pageUrl","shortenedFileNameLength","URL","originalSrc","toString","toFileSystemPath","sanitizedSrc","pathname","preserveExternalDomain","ensureNonDirectory","encodeQueryString","truncateFileName","removeSpecialChars","addExtension","endsWith","queryString","search","safeQueryString","hash","pathPieces","split","rebuiltPieces","forEach","piece","index","shortName","length","substring","push","startsWith","replace","contentType","nameWithExtension","extname","fileExtension","getExtension","fullUrl","origin","encodeURIComponent","host","name","update","digest","NodeType","srcset","CSS_URL_REGEX","DOMSnapshot","snapshot","Buffer","isBuffer","bufferAsString","JSON","parse","mapAssetPaths","sourceMap","transformedSnapshot","mapNode","stringify","node","mapNodeAttributes","mapTextElement","childNodes","Promise","all","map","childNode","type","Element","attributes","src","sourceVal","has","get","style","cssText","mappedCssText","mapCssUrls","_cssText","tagName","srcsetValue","currentSrc","mapSrcsetUrls","sizes","Text","isStyle","textContent","match","cssUrl","parsedSrcset","set","sanitize","__name","string","toLowerCase","writeTestResult","e2eTestInfo","domSnapshots","archive","chromaticStorybookParams","title","outputDir","finalOutputDir","archiveDir","Map","Object","entries","archiveFile","origSrcPath","fileSystemPath","body","domSnapshot","mappedSnapshot","writeStoriesFile","errors","filter","r","fromEntries","storiesFilename","stories","keys","parameters","server","id","chromatic","Watcher","cdpClient","allowedDomains","client","allowedArchiveOrigins","firstUrl","domain","watch","on","requestPaused","bind","send","clientSend","request","method","params","error","requestId","responseStatusCode","responseStatusText","responseErrorReason","responseHeaders","requestUrl","isRequestFromAllowedDomain","includes","interceptResponse","result","base64Encoded","contentTypeHeader","find","isFirstRequest","statusCode","statusText","from","value","writeArchives","testTitle","resourceArchive","bufferedArchiveList","key","allSnapshots","item","watcher","port","setupNetworkListener","webSocketDebuggerUrl","cdp","target","err","saveArchives","archiveInfo","resolve","then","prepareArchives","action","payload","onBeforeBrowserLaunch","browser","launchOptions","hostArg","args","arg","portArg","portString","ELECTRON_EXTRA_LAUNCH_ARGS","parseInt","installPlugin"],"mappings":"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","sourcesContent":["import type { elementNode } from 'rrweb-snapshot';\nimport CDP, { Version } from 'chrome-remote-interface';\nimport { writeTestResult } from '../write-archive';\nimport type { ChromaticStorybookParameters } from '../types';\nimport type { ResourceArchive } from '../resource-archive';\nimport { Watcher } from '../resource-archive';\n\ninterface WriteParams {\n  testTitle: string;\n  domSnapshots: elementNode[];\n  chromaticStorybookParams: ChromaticStorybookParameters;\n  pageUrl: string;\n}\n\ninterface WriteArchivesParams extends WriteParams {\n  resourceArchive: ResourceArchive;\n}\n\nconst writeArchives = async ({\n  testTitle,\n  domSnapshots,\n  resourceArchive,\n  chromaticStorybookParams,\n  pageUrl,\n}: WriteArchivesParams) => {\n  const bufferedArchiveList = Object.entries(resourceArchive).map(([key, value]) => {\n    return [\n      key,\n      {\n        ...value,\n        // we can't use Buffer in the browser (when we collect the responses)\n        // so we go through one by one here and bufferize them\n        // @ts-expect-error will fix when Cypress has its own package\n        body: Buffer.from(value.body, 'utf8'),\n      },\n    ];\n  });\n\n  const allSnapshots = Object.fromEntries(\n    domSnapshots.map((item, index) => [`Snapshot #${index + 1}`, Buffer.from(JSON.stringify(item))])\n  );\n\n  await writeTestResult(\n    {\n      title: testTitle,\n      // this will store it at ./cypress/downloads (the last directory doesn't matter)\n      // TODO: change so we don't have to do this trickery\n      outputDir: './cypress/downloads/some',\n      pageUrl,\n    },\n    allSnapshots,\n    Object.fromEntries(bufferedArchiveList),\n    chromaticStorybookParams\n  );\n};\n\n// using a single Watcher instance across all tests (for the test run)\n// each time a test completes, we'll save to disk whatever archives are there at that point.\n// This should be safe since the same resource from the same URL should be the same during the entire test run.\n// Cypress doesn't give us a way to share variables between the \"before test\" and \"after test\" lifecycle events on the server.\nlet watcher: Watcher = null;\n\nlet host = '';\nlet port = 0;\n\nconst setupNetworkListener = async (): Promise<null> => {\n  try {\n    const { webSocketDebuggerUrl } = await Version({\n      host,\n      port,\n    });\n\n    const cdp = await CDP({\n      target: webSocketDebuggerUrl,\n    });\n\n    if (!watcher) {\n      watcher = new Watcher(cdp);\n      await watcher.watch();\n    }\n  } catch (err) {\n    console.log('err', err);\n  }\n\n  return null;\n};\n\nconst saveArchives = (archiveInfo: WriteParams) => {\n  return new Promise((resolve) => {\n    // the watcher's archives come from the server, everything else (DOM snapshots, test info, etc) comes from the browser\n    // notice we're not calling + awaiting watcher.idle() here...\n    // that's because in Cypress, cy.visit() waits until all resources have loaded before finishing\n    // so at this point (after the test) we're confident that the resources are all there already without having to wait more\n    return writeArchives({ ...archiveInfo, resourceArchive: watcher.archive }).then(() => {\n      resolve(null);\n    });\n  });\n};\n\ninterface TaskParams {\n  action: 'setup-network-listener' | 'save-archives';\n  payload?: any;\n}\n\n// Handles all server-side tasks, dispatching each to its proper handler.\n// Why? So users don't have to register all these individual tasks\n// (they can just import and register prepareArchives)\nexport const prepareArchives = async ({ action, payload }: TaskParams) => {\n  switch (action) {\n    case 'setup-network-listener':\n      return setupNetworkListener();\n    case 'save-archives':\n      return saveArchives(payload);\n    default:\n      return null;\n  }\n};\n\n// We use this lifecycle hook because we need to know what host and port Chrome Devtools Protocol is listening at.\nexport const onBeforeBrowserLaunch = (\n  // we don't use the browser parameter but we're keeping it here in case we'd ever need to read from it\n  // (this way users wouldn't have to change their cypress.config file as it's already passed to us)\n  browser: Cypress.Browser,\n  launchOptions: Cypress.BeforeBrowserLaunchOptions\n) => {\n  const hostArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-address='));\n  host = hostArg ? hostArg.split('=')[1] : '127.0.0.1';\n\n  const portArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-port='));\n  let portString = '';\n\n  if (portArg) {\n    [, portString] = portArg.split('=');\n  } else {\n    // Electron doesn't pass along the address and port in the launch options, so we need to read the port from the\n    // environment variable that we'll require the user to use (this assumes the host will be 127.0.0.1).\n    const entry = process.env.ELECTRON_EXTRA_LAUNCH_ARGS.split(' ').find((item) =>\n      item.startsWith('--remote-debugging-port')\n    );\n    [, portString] = entry.split('=');\n  }\n\n  port = parseInt(portString, 10);\n\n  return launchOptions;\n};\n\nexport const installPlugin = (on: any) => {\n  // these events are run on the server (in Node)\n  on('task', {\n    prepareArchives,\n  });\n  on('before:browser:launch', onBeforeBrowserLaunch);\n};\n","import { outputFile, ensureDir, outputJson } from 'fs-extra';\nimport { join } from 'path';\nimport { logger } from '../utils/logger';\nimport { ArchiveFile } from './archive-file';\nimport { DOMSnapshot } from './dom-snapshot';\nimport type { ResourceArchive } from '../resource-archive';\nimport type { ChromaticStorybookParameters } from '../types';\n\n// @storybook/csf's sanitize function, we could import this\nexport const sanitize = (string: string) => {\n  return (\n    string\n      .toLowerCase()\n      // eslint-disable-next-line no-useless-escape\n      .replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\\-=?;:'\",.<>\\{\\}\\[\\]\\\\\\/]/gi, '-')\n      .replace(/-+/g, '-')\n      .replace(/^-+/, '')\n      .replace(/-+$/, '')\n  );\n};\n\n// We write a collection of DOM snapshots and a resource archive in the following locations:\n// <test-title>.stories.json\n// archive/<test-title>.json\n// archive/<file>.<ext>\n\ninterface E2ETestInfo {\n  title: string;\n  outputDir: string;\n  pageUrl: string;\n}\n\nexport async function writeTestResult(\n  e2eTestInfo: E2ETestInfo,\n  domSnapshots: Record<string, Buffer>,\n  archive: ResourceArchive,\n  chromaticStorybookParams: ChromaticStorybookParameters\n) {\n  const { title, outputDir, pageUrl } = e2eTestInfo;\n  // outputDir gives us the test-specific subfolder (https://playwright.dev/docs/api/class-testconfig#test-config-output-dir);\n  // we want to write one level above that\n  const finalOutputDir = join(outputDir, '..', 'chromatic-archives');\n\n  const archiveDir = join(finalOutputDir, 'archive');\n\n  await ensureDir(finalOutputDir);\n\n  logger.log(`Writing test results for \"${title}\"`);\n\n  // Used to store any changes to the asset paths made in order to save them to the file system\n  // so that we can update the `src` attributes in the DOM snapshots.\n  const sourceMap = new Map<string, string>();\n\n  await Promise.all(\n    Object.entries(archive).map(async ([url, response]) => {\n      if ('error' in response) return;\n\n      const archiveFile = new ArchiveFile(url, response, pageUrl);\n      const origSrcPath = archiveFile.originalSrc();\n      const fileSystemPath = archiveFile.toFileSystemPath();\n\n      if (origSrcPath !== fileSystemPath) {\n        sourceMap.set(origSrcPath, fileSystemPath);\n      }\n\n      await outputFile(join(archiveDir, fileSystemPath), response.body);\n    })\n  );\n\n  await Promise.all(\n    await Object.entries(domSnapshots).map(async ([name, domSnapshot]) => {\n      // XXX_jwir3: We go through our stories here and map any instances that are found in\n      //            the keys of the source map to their respective values.\n      const snapshot = new DOMSnapshot(domSnapshot);\n      const mappedSnapshot = await snapshot.mapAssetPaths(sourceMap);\n\n      await outputFile(\n        join(archiveDir, `${sanitize(title)}-${sanitize(name)}.snapshot.json`),\n        mappedSnapshot\n      );\n    })\n  );\n\n  await writeStoriesFile(\n    join(finalOutputDir, `${sanitize(title)}.stories.json`),\n    title,\n    domSnapshots,\n    chromaticStorybookParams\n  );\n\n  const errors = Object.entries(archive).filter(([, r]) => 'error' in r);\n  if (errors.length > 0) {\n    logger.log(`Encountered ${errors.length} errors archiving resources, writing to 'errors.json'`);\n    await outputJson(join(archiveDir, `errors.json`), {\n      errors: Object.fromEntries(errors),\n    });\n  }\n}\n\nasync function writeStoriesFile(\n  storiesFilename: string,\n  title: string,\n  domSnapshots: Record<string, Buffer>,\n  chromaticStorybookParams: ChromaticStorybookParameters\n) {\n  logger.log(`Writing ${storiesFilename}`);\n  await outputJson(storiesFilename, {\n    title,\n    stories: Object.keys(domSnapshots).map((name) => ({\n      name,\n      parameters: {\n        server: { id: `${sanitize(title)}-${sanitize(name)}.snapshot.json` },\n        chromatic: {\n          ...chromaticStorybookParams,\n        },\n      },\n    })),\n  });\n}\n","export const logger = process.env.LOG\n  ? console\n  : { log: (..._args: any[]) => {}, warn: (..._args: any[]) => {} };\n","import mime from 'mime';\nimport path from 'path';\nimport { createHash } from 'node:crypto';\nimport { logger } from '../utils/logger';\nimport type { ArchiveResponse, UrlString } from '../resource-archive';\n\n/**\n * Handles converting the full URL of assets loaded during a test\n * captured in the resource archive into a file system friendly path so\n * that they can be safely written to the file system.\n *\n * This includes:\n * - ensuring the path has a file name to avoid colliding with a directory\n * - encoding the query string into the file name to avoid overwriting files\n *   that have a different response due to the query string\n * - truncating file names and path parts that are too big for some file systems\n * - removing problematic characters\n * - ensuring the file name has an extension\n */\nexport class ArchiveFile {\n  url: URL;\n\n  response: ArchiveResponse;\n\n  shortenedFileNameLength: number;\n\n  pageUrl: URL;\n\n  constructor(url: UrlString, response: ArchiveResponse, pageUrl: UrlString) {\n    this.url = new URL(url);\n    this.response = response;\n    this.shortenedFileNameLength = 250;\n    this.pageUrl = new URL(pageUrl);\n  }\n\n  originalSrc() {\n    return this.url.toString();\n  }\n\n  toFileSystemPath() {\n    let sanitizedSrc = this.url.pathname;\n\n    sanitizedSrc = this.preserveExternalDomain(this.url);\n    sanitizedSrc = this.ensureNonDirectory(sanitizedSrc);\n    sanitizedSrc = this.encodeQueryString(sanitizedSrc);\n    sanitizedSrc = this.truncateFileName(sanitizedSrc);\n    sanitizedSrc = this.removeSpecialChars(sanitizedSrc);\n    sanitizedSrc = this.addExtension(sanitizedSrc);\n\n    return sanitizedSrc;\n  }\n\n  private ensureNonDirectory(pathname: string) {\n    return pathname.endsWith('/') ? `${pathname}index.html` : pathname;\n  }\n\n  private encodeQueryString(pathname: string) {\n    const queryString = this.url.search;\n    if (!queryString) return pathname;\n\n    const safeQueryString = this.hash(queryString);\n    return `${pathname}-${safeQueryString}`;\n  }\n\n  private truncateFileName(pathname: string) {\n    // Split this path to get all of the pieces\n    const pathPieces = pathname.split('/');\n    const rebuiltPieces: Array<string> = [];\n\n    // Shorten each individual piece\n    pathPieces.forEach((piece, index) => {\n      let shortName: string = piece;\n      if (piece.length > this.shortenedFileNameLength) {\n        shortName = piece.substring(0, this.shortenedFileNameLength);\n        logger.log(`Chunk of filename '${piece}' is too long. Shortening to '${shortName}'`);\n      }\n\n      rebuiltPieces.push(shortName);\n    });\n\n    // Re-join the pieces\n    return `${pathname.startsWith('/') ? '/' : ''}${path.join(...rebuiltPieces)}`;\n  }\n\n  private removeSpecialChars(pathname: string) {\n    // The storybook server seems to have a problem with percents in file names\n    return pathname.replace(/[%]/g, '');\n  }\n\n  private addExtension(pathname: string) {\n    if ('error' in this.response || !this.response.contentType) return pathname;\n\n    // Add an extension if needed\n    let nameWithExtension = pathname;\n    if (!path.extname(nameWithExtension)) {\n      const fileExtension = mime.getExtension(this.response.contentType);\n      if (fileExtension) {\n        nameWithExtension = `${pathname}.${fileExtension}`;\n      }\n    }\n\n    return nameWithExtension;\n  }\n\n  private preserveExternalDomain(fullUrl: URL) {\n    if (fullUrl.origin === this.pageUrl.origin) {\n      return fullUrl.pathname;\n    }\n\n    // Windows doesn't support colons in file names\n    const encodedHost = encodeURIComponent(fullUrl.host);\n\n    return `/${encodedHost}/${fullUrl.pathname}`;\n  }\n\n  private hash(name: string) {\n    return createHash('md5').update(name).digest('hex');\n  }\n}\n","/* eslint-disable no-underscore-dangle */\n/* eslint-disable no-param-reassign */\nimport type { serializedNodeWithId } from 'rrweb-snapshot';\nimport { NodeType } from 'rrweb-snapshot';\nimport srcset from 'srcset';\n\n// Matches `url(...)` function in CSS text, excluding data URLs\nconst CSS_URL_REGEX = /url\\((?!['\"]?(?:data):)['\"]?([^'\")]*)['\"]?\\)/gi;\n\n/**\n * Wraps a snapshot from rrweb and handles post-processing to remap asset paths.\n */\nexport class DOMSnapshot {\n  snapshot: serializedNodeWithId;\n\n  constructor(snapshot: Buffer | string) {\n    if (Buffer.isBuffer(snapshot)) {\n      const bufferAsString = snapshot.toString('utf-8');\n      this.snapshot = JSON.parse(bufferAsString);\n    } else {\n      this.snapshot = JSON.parse(snapshot);\n    }\n  }\n\n  async mapAssetPaths(sourceMap: Map<string, string>) {\n    const transformedSnapshot = await this.mapNode(this.snapshot, sourceMap);\n    return JSON.stringify(transformedSnapshot);\n  }\n\n  private async mapNode(node: serializedNodeWithId, sourceMap: Map<string, string>) {\n    node = this.mapNodeAttributes(node, sourceMap);\n    node = this.mapTextElement(node, sourceMap);\n\n    if ('childNodes' in node) {\n      node.childNodes = await Promise.all(\n        node.childNodes.map(async (childNode) => {\n          return this.mapNode(childNode, sourceMap);\n        })\n      );\n    }\n\n    return node;\n  }\n\n  private mapNodeAttributes(node: serializedNodeWithId, sourceMap: Map<string, string>) {\n    if (node.type === NodeType.Element) {\n      if (node.attributes.src) {\n        const sourceVal = node.attributes.src as string;\n        if (sourceMap.has(sourceVal)) {\n          node.attributes.src = sourceMap.get(sourceVal);\n        }\n      }\n\n      if (node.attributes.style) {\n        const cssText = node.attributes.style as string;\n        const mappedCssText = this.mapCssUrls(cssText, sourceMap);\n        node.attributes.style = mappedCssText;\n      }\n\n      // This is how rrweb stores the contents of an external stylesheet\n      // that it will put into a `style` tag on render\n      if (node.attributes._cssText) {\n        const cssText = node.attributes._cssText as string;\n        const mappedCssText = this.mapCssUrls(cssText, sourceMap);\n        node.attributes._cssText = mappedCssText;\n      }\n\n      // When an image tag has the `srcset` attributes, the browser will choose one of the images\n      // from the `srcset` list to load at render time based on the viewport size. To support this,\n      // we parse the URLs in the `srcset` attribute and try to find a match in the asset map.\n      // If a match is found, we'll overwrite the `src` attribute with the mapped asset path,\n      // and we'll remove the `srcset` and `sizes` attributes because we'll only have captured\n      // the one asset that the browser decided to load when this was rendered. We don't want\n      // the browser to try to load one of the others when this snapshot is rendered in Chromatic\n      // because we won't have archived them.\n      if (node.tagName === 'img' && node.attributes.srcset) {\n        const srcsetValue = node.attributes.srcset as string;\n        const currentSrc = this.mapSrcsetUrls(srcsetValue, sourceMap);\n        if (currentSrc) {\n          node.attributes.src = currentSrc;\n\n          // Remove srcset attributes since we'll only have the one that\n          // loaded on render archived\n          delete node.attributes.srcset;\n          delete node.attributes.sizes;\n        }\n      }\n    }\n\n    return node;\n  }\n\n  private mapTextElement(node: serializedNodeWithId, sourceMap: Map<string, string>) {\n    if (node.type === NodeType.Text && node.isStyle) {\n      if (node.textContent) {\n        const mappedCssText = this.mapCssUrls(node.textContent, sourceMap);\n        node.textContent = mappedCssText;\n      }\n    }\n\n    return node;\n  }\n\n  private mapCssUrls(cssText: string, sourceMap: Map<string, string>) {\n    return cssText.replace(CSS_URL_REGEX, (match, fullUrl) => {\n      let cssUrl = match;\n      if (sourceMap.has(fullUrl)) {\n        cssUrl = match.replace(fullUrl, sourceMap.get(fullUrl));\n      }\n      return cssUrl;\n    });\n  }\n\n  private mapSrcsetUrls(srcsetValue: string, sourceMap: Map<string, string>) {\n    const parsedSrcset = srcset.parse(srcsetValue);\n    let currentSrc;\n    parsedSrcset.forEach((set) => {\n      if (sourceMap.has(set.url)) {\n        currentSrc = sourceMap.get(set.url);\n      }\n    });\n\n    return currentSrc;\n  }\n}\n/* eslint-enable no-underscore-dangle */\n/* eslint-enable no-param-reassign */\n","import type { Protocol } from 'playwright-core/types/protocol';\nimport { logger } from '../utils/logger';\n\nexport type UrlString = string;\n\nexport type ArchiveResponse =\n  | {\n      statusCode: number;\n      statusText?: string;\n      body: Buffer;\n      contentType?: string;\n    }\n  | {\n      error: Error;\n    };\n\nexport type ResourceArchive = Record<UrlString, ArchiveResponse>;\n\n// a custom interface that satisfies both playwright's CDPSession and chrome-remote-interface's CDP.Client types.\ninterface CDPClient {\n  on: (eventName: keyof Protocol.Events, handlerFunction: (params?: any) => void) => void;\n  send: (eventName: keyof Protocol.CommandParameters, payload?: any) => Promise<any>;\n}\n\nexport class Watcher {\n  public archive: ResourceArchive = {};\n\n  private client: CDPClient;\n\n  /** \n   Specifies which domains (origins) we should archive resources for (by default we only archive same-origin resources).\n   Useful in situations where the environment running the archived storybook (e.g. in CI) may be restricted to an intranet or other domain restrictions\n  */\n  private allowedArchiveOrigins: string[];\n\n  /**\n   * We assume the first URL loaded after @watch is called is the base URL of the\n   * page and we only save resources that are loaded from the same protocol/host/port combination.\n   * We also skip archiving this page because we only care about resources requested by this page.\n   */\n  private firstUrl: URL;\n\n  constructor(cdpClient: CDPClient, allowedDomains?: string[]) {\n    this.client = cdpClient;\n    // tack on the protocol so we can properly check if requests are cross-origin\n    this.allowedArchiveOrigins = (allowedDomains || []).map((domain) => `https://${domain}`);\n  }\n\n  async watch() {\n    this.client.on('Fetch.requestPaused', this.requestPaused.bind(this));\n    await this.client.send('Fetch.enable');\n  }\n\n  async clientSend<T extends keyof Protocol.CommandParameters>(\n    request: Protocol.Network.Request,\n    method: T,\n    params?: Protocol.CommandParameters[T]\n  ): Promise<Protocol.CommandReturnValues[T] | null> {\n    try {\n      return await this.client.send(method, params);\n    } catch (error) {\n      logger.log('Client error', request.url, error);\n      this.archive[request.url] = { error };\n      return null;\n    }\n  }\n\n  async requestPaused({\n    requestId,\n    request,\n    responseStatusCode,\n    responseStatusText,\n    responseErrorReason,\n    responseHeaders,\n  }: Protocol.Fetch.requestPausedPayload) {\n    // We only need to capture assets that will render when the DOM snapshot is rendered,\n    // so we only need to handle GET requests.\n    if (!request.method.match(/get/i)) {\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    // There's no reponse body for us to archive on 304s\n    if (responseStatusCode === 304) {\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    const requestUrl = new URL(request.url);\n\n    this.firstUrl ??= requestUrl;\n\n    const isRequestFromAllowedDomain =\n      requestUrl.origin === this.firstUrl.origin ||\n      this.allowedArchiveOrigins.includes(requestUrl.origin);\n\n    logger.log(\n      'requestPaused',\n      requestUrl.toString(),\n      responseStatusCode || responseErrorReason ? 'response' : 'request',\n      this.firstUrl.toString(),\n      isRequestFromAllowedDomain\n    );\n\n    // Pausing at response stage with an error, simply ignore\n    if (responseErrorReason) {\n      logger.log(`Got response error: ${responseErrorReason}`);\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    // Pausing a response stage with a response\n    if (responseStatusCode) {\n      if ([301, 302, 307, 308].includes(responseStatusCode)) {\n        await this.clientSend(request, 'Fetch.continueRequest', {\n          requestId,\n          interceptResponse: true,\n        });\n        return;\n      }\n\n      const result = await this.clientSend(request, 'Fetch.getResponseBody', {\n        requestId,\n      });\n      // Something has gone wrong and will be logged above\n      if (result === null) {\n        return;\n      }\n      const { body, base64Encoded } = result;\n\n      // If the Content-Type header is present, let's capture it.\n      const contentTypeHeader: Protocol.Fetch.HeaderEntry = responseHeaders.find(\n        ({ name }) => name.toLowerCase() === 'content-type'\n      );\n\n      // No need to capture the response of the top level page request\n      const isFirstRequest = requestUrl.toString() === this.firstUrl.toString();\n      if (isRequestFromAllowedDomain && !isFirstRequest) {\n        this.archive[request.url] = {\n          statusCode: responseStatusCode,\n          statusText: responseStatusText,\n          body: Buffer.from(body, base64Encoded ? 'base64' : 'utf8'),\n          contentType: contentTypeHeader?.value,\n        };\n      }\n\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    await this.clientSend(request, 'Fetch.continueRequest', {\n      requestId,\n      interceptResponse: true,\n    });\n  }\n}\n"]}