@authless/puppeteer-extra-plugin-har
Version:
> This is a plugin for [puppeteer-extra](https://github.com/berstend/puppeteer-extra) that allows you to create a HAR file from your page.
109 lines (93 loc) • 3.47 kB
text/typescript
import { PuppeteerExtraPlugin } from 'puppeteer-extra-plugin'
import PuppeteerHar from '@authless/puppeteer-har'
/* eslint-disable-next-line @typescript-eslint/no-empty-interface */
export interface HarConfig {
callback?: HarCallback
}
type TargetId = string
type BrowserProcessId = string
type HarId = string
type HarObject = any
type HarCallback = (error: Error | null, data: { har: HarObject, harId: HarId } | null) => Promise<void>
export class Har extends PuppeteerExtraPlugin {
/**
* manages {@link PuppeteerHar} instances by assigning them to their {@link TargetId}
*/
protected targetMap: Map<HarId, PuppeteerHar>
/* eslint-disable-next-line no-empty-function */
protected readonly harCallback: HarCallback = async () => {}
constructor (config: HarConfig = {}) {
super(config)
this.targetMap = new Map<HarId, PuppeteerHar>()
if (typeof config.callback !== 'undefined') {
this.harCallback = config.callback
}
}
/* eslint-disable-next-line class-methods-use-this */
get name (): string {
return 'har'
}
private static getTargetId (target: any): TargetId {
const targetUrl: string = target.url()
const targetId = target._targetInfo?.targetId
if (typeof targetId !== 'string') {
throw new Error(`failed to get target id for target: ${targetUrl} (type: ${target.type() as string})`)
}
return targetId
}
private static getBrowserProcessId (target: any): BrowserProcessId {
const targetUrl: string = target.url()
const browser = target.browser()
const processId = `${browser._process?.pid as string}`
if (typeof processId !== 'string') {
throw new Error(`failed to get browser process id for target: ${targetUrl} (type: ${target.type() as string})`)
}
return processId
}
private static getHarId (target: any): HarId {
return `${this.getBrowserProcessId(target)}-${this.getTargetId(target)}`
}
protected async startRecording (target): Promise<void> {
const harId = Har.getHarId(target)
const page = await target.page()
const har = new PuppeteerHar(page)
await har.start()
this.debug(`recording HAR [ID: ${harId}]`)
this.targetMap.set(harId, har)
}
protected async finishRecording (harId: HarId): Promise<void> {
const harInstance = this.targetMap.get(harId)
if (harInstance instanceof PuppeteerHar) {
this.targetMap.delete(harId)
try {
let start = Date.now()
const harObject = await harInstance.stop()
this.debug(`recorded HAR [ID: ${harId}]. (serialized in ${Date.now() - start}ms)`)
await this.harCallback(null, { har: harObject, harId })
} catch (error) {
await this.harCallback(error, null)
}
}
}
async onTargetCreated (target): Promise<void> {
if (target.type() === 'page') {
await this.startRecording(target)
}
}
async onTargetDestroyed (target): Promise<void> {
if (target.type() === 'page') {
const harId = Har.getHarId(target)
await this.finishRecording(harId)
}
}
async onDisconnected (): Promise<void> {
if (this.targetMap.size > 0) {
this.debug.extend('disconnect')(`open recordings discovered: ${this.targetMap.size}`)
const openHarIds = Array.from(this.targetMap.keys())
await Promise.all(openHarIds.map(async harId => {
/* eslint-disable-next-line no-return-await */
return await this.finishRecording(harId)
}))
}
}
}