Skip to content

Instantly share code, notes, and snippets.

@mizchi
Created May 14, 2025 15:56
Show Gist options
  • Save mizchi/9334e465810c7de83b1b21191994f9d1 to your computer and use it in GitHub Desktop.
Save mizchi/9334e465810c7de83b1b21191994f9d1 to your computer and use it in GitHub Desktop.
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