Created
May 14, 2025 15:56
-
-
Save mizchi/9334e465810c7de83b1b21191994f9d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import puppeteer, { | |
ActiveSession, | |
Browser, | |
BrowserWorker, | |
Page, | |
} from "@cloudflare/puppeteer"; | |
import { DurableObject } from "cloudflare:workers"; | |
export class BrowserObject extends DurableObject<Env> { | |
env: Env; | |
browser: Browser | undefined = undefined; | |
constructor(state: DurableObjectState, env: Env) { | |
super(state, env); | |
this.env = env; | |
console.log("BrowserObject constructor"); | |
} | |
private async getBrowser() { | |
const sessionId = await this.getRandomSession(this.env.MYBROWSER); | |
if (sessionId) { | |
return await puppeteer.connect(this.env.MYBROWSER, sessionId); | |
} else { | |
// throw new Error("No available session"); | |
return await puppeteer.launch(this.env.MYBROWSER); | |
} | |
} | |
// Pick random free session | |
// Other custom logic could be used instead | |
async getRandomSession(endpoint: BrowserWorker): Promise<string | undefined> { | |
const sessions: ActiveSession[] = await puppeteer.sessions(endpoint); | |
console.log(`Sessions: ${JSON.stringify(sessions)}`); | |
const sessionsIds = sessions | |
.filter((v) => { | |
return !v.connectionId; // remove sessions with workers connected to them | |
}) | |
.map((v) => { | |
return v.sessionId; | |
}); | |
if (sessionsIds.length === 0) { | |
return; | |
} | |
const sessionId = | |
sessionsIds[Math.floor(Math.random() * sessionsIds.length)]; | |
return sessionId!; | |
} | |
public async getMetrics() { | |
const browser = await this.getBrowser(); | |
const page = await browser.newPage(); | |
page.on("console", (msg) => { | |
console.log("[console]", msg.type(), msg.text()); | |
}); | |
await page.evaluateOnNewDocument(() => { | |
new PerformanceObserver((l: any) => { | |
const entries = l.getEntries(); | |
// the last entry is the largest contentful paint | |
const largestPaintEntry = entries.at(-1); | |
// @ts-ignore | |
globalThis.largestPaintEntry = largestPaintEntry.startTime; | |
console.log("LCP", largestPaintEntry.startTime); | |
}).observe({ | |
type: "largest-contentful-paint", | |
buffered: true, | |
}); | |
// }); | |
}); | |
await page.goto("https://zenn.dev", { | |
waitUntil: "networkidle2", | |
}); | |
await page.waitForFunction(() => { | |
// @ts-ignore | |
return globalThis.largestPaintEntry !== undefined; | |
// }); | |
}); | |
const v = await page.evaluate(() => { | |
// @ts-ignore | |
return globalThis.largestPaintEntry; | |
}); | |
// const v = await getLcp(page); | |
// await | |
const metrics = await page.metrics(); | |
// await page.wait | |
await browser.disconnect(); | |
return { | |
lcp: v, | |
metrics, | |
}; | |
} | |
} | |
export default { | |
async fetch(request, env, ctx): Promise<Response> { | |
const id = env.BrowserObject.idFromName("browser"); | |
const bo = env.BrowserObject.get(id); | |
const res = await bo.getMetrics(); | |
console.log("Metrics", res); | |
return Response.json(res); | |
}, | |
} satisfies ExportedHandler<Env>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment