1- // Browser abstraction layer using browser-commander utilities
2- // Note: browser-commander's launchBrowser currently doesn't support custom args
3- // for headless server environments (--no-sandbox, etc). We use browser-commander's
4- // utilities (CHROME_ARGS) and maintain browser launch code here until this
5- // feature is added to browser-commander.
6- // See: https://github.com/link-foundation/browser-commander/issues/11
1+ // Browser abstraction layer using browser-commander for all browser operations
2+ // See: https://github.com/link-foundation/browser-commander
73
8- import { CHROME_ARGS } from 'browser-commander' ;
9- import puppeteer from 'puppeteer ' ;
10- import playwright from 'playwright ' ;
4+ import { launchBrowser } from 'browser-commander' ;
5+ import os from 'os ' ;
6+ import path from 'path ' ;
117
128/**
139 * Additional Chrome args needed for headless server environments
14- * These are not included in browser-commander's CHROME_ARGS yet
10+ * These are appended to browser-commander's default CHROME_ARGS
1511 */
1612const SERVER_CHROME_ARGS = [
1713 '--no-sandbox' ,
1814 '--disable-setuid-sandbox' ,
1915 '--disable-dev-shm-usage' ,
2016] ;
2117
22- /**
23- * Combined Chrome args: browser-commander defaults + server-specific args
24- */
25- const ALL_CHROME_ARGS = [ ...CHROME_ARGS , ...SERVER_CHROME_ARGS ] ;
26-
2718/**
2819 * Unified browser interface that works with both Puppeteer and Playwright
2920 * @typedef {Object } BrowserAdapter
@@ -48,70 +39,52 @@ const ALL_CHROME_ARGS = [...CHROME_ARGS, ...SERVER_CHROME_ARGS];
4839
4940/**
5041 * Create a browser instance using the specified engine
42+ * Uses browser-commander's launchBrowser for both Puppeteer and Playwright
5143 * @param {string } engine - 'puppeteer' or 'playwright' (defaults to puppeteer)
5244 * @param {Object } options - Browser launch options
5345 * @returns {Promise<BrowserAdapter> }
5446 */
5547export async function createBrowser ( engine = 'puppeteer' , options = { } ) {
5648 const normalizedEngine = engine . toLowerCase ( ) ;
57-
58- if ( normalizedEngine === 'playwright' ) {
59- return await createPlaywrightBrowser ( options ) ;
60- } else {
61- return await createPuppeteerBrowser ( options ) ;
62- }
63- }
64-
65- /**
66- * Create a Puppeteer browser instance
67- * @param {Object } options - Puppeteer launch options
68- * @returns {Promise<BrowserAdapter> }
69- */
70- async function createPuppeteerBrowser ( options = { } ) {
71- const defaultOptions = {
72- args : ALL_CHROME_ARGS ,
73- } ;
74-
75- const browser = await puppeteer . launch ( { ...defaultOptions , ...options } ) ;
76-
77- return {
78- async newPage ( ) {
79- const page = await browser . newPage ( ) ;
80- return createPuppeteerPageAdapter ( page ) ;
81- } ,
82- async close ( ) {
83- await browser . close ( ) ;
84- } ,
85- type : 'puppeteer' ,
86- _browser : browser ,
87- } ;
88- }
89-
90- /**
91- * Create a Playwright browser instance
92- * @param {Object } options - Playwright launch options
93- * @returns {Promise<BrowserAdapter> }
94- */
95- async function createPlaywrightBrowser ( options = { } ) {
96- const defaultOptions = {
97- args : ALL_CHROME_ARGS ,
98- } ;
99-
100- // Playwright uses chromium by default
101- const browser = await playwright . chromium . launch ( {
102- ...defaultOptions ,
49+ const engineType =
50+ normalizedEngine === 'playwright' || normalizedEngine === 'pw'
51+ ? 'playwright'
52+ : 'puppeteer' ;
53+
54+ // Generate unique userDataDir for this session to avoid conflicts
55+ const userDataDir = path . join (
56+ os . tmpdir ( ) ,
57+ `web-capture-${ engineType } -${ Date . now ( ) } `
58+ ) ;
59+
60+ // Use browser-commander's launchBrowser with server-specific args
61+ // Default to headless for server environments
62+ const { browser, page } = await launchBrowser ( {
63+ engine : engineType ,
64+ args : SERVER_CHROME_ARGS ,
65+ headless : true ,
66+ userDataDir,
67+ slowMo : 0 , // Disable slowMo for server operations
10368 ...options ,
10469 } ) ;
10570
71+ // Close the initial page since we'll create new ones via newPage()
72+ await page . close ( ) ;
73+
74+ const pageAdapter =
75+ engineType === 'playwright'
76+ ? createPlaywrightPageAdapter
77+ : createPuppeteerPageAdapter ;
78+
10679 return {
10780 async newPage ( ) {
108- const page = await browser . newPage ( ) ;
109- return createPlaywrightPageAdapter ( page ) ;
81+ const newPage = await browser . newPage ( ) ;
82+ return pageAdapter ( newPage ) ;
11083 } ,
11184 async close ( ) {
11285 await browser . close ( ) ;
11386 } ,
114- type : 'playwright' ,
87+ type : engineType ,
11588 _browser : browser ,
11689 } ;
11790}
@@ -191,7 +164,6 @@ function createPlaywrightPageAdapter(page) {
191164
192165/**
193166 * Get the browser engine from query parameters or environment variable
194- * Uses browser-commander's detectEngine when available, with fallback logic
195167 * @param {Object } req - Express request object
196168 * @returns {string } - 'puppeteer' or 'playwright'
197169 */
0 commit comments