@things-factory/shell 10.0.0-beta.7 → 10.0.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BrowserFactory = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
4
5
|
const env_1 = require("@things-factory/env");
|
|
5
6
|
// Dynamic puppeteer loading with error handling
|
|
6
7
|
let puppeteer;
|
|
@@ -10,6 +11,23 @@ try {
|
|
|
10
11
|
catch (err) {
|
|
11
12
|
env_1.logger.warn('Puppeteer not available:', err);
|
|
12
13
|
}
|
|
14
|
+
// Detect the best GL backend for the current environment (cached)
|
|
15
|
+
let detectedGLBackend = null;
|
|
16
|
+
function detectGLBackend() {
|
|
17
|
+
if (detectedGLBackend !== null)
|
|
18
|
+
return detectedGLBackend;
|
|
19
|
+
if (process.platform === 'darwin') {
|
|
20
|
+
detectedGLBackend = 'angle';
|
|
21
|
+
}
|
|
22
|
+
else if ((0, fs_1.existsSync)('/dev/nvidia0') || (0, fs_1.existsSync)('/dev/dri/renderD128')) {
|
|
23
|
+
detectedGLBackend = 'egl';
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
detectedGLBackend = 'swiftshader';
|
|
27
|
+
}
|
|
28
|
+
env_1.logger.info(`GL backend detected: ${detectedGLBackend}`);
|
|
29
|
+
return detectedGLBackend;
|
|
30
|
+
}
|
|
13
31
|
/**
|
|
14
32
|
* Browser Factory for creating and managing browser instances
|
|
15
33
|
*/
|
|
@@ -86,9 +104,11 @@ class BrowserFactory {
|
|
|
86
104
|
}
|
|
87
105
|
}
|
|
88
106
|
static getLaunchSettings(poolConfig) {
|
|
107
|
+
const glBackend = detectGLBackend();
|
|
108
|
+
const args = (poolConfig.args || []).map(arg => arg.startsWith('--use-gl=') ? `--use-gl=${glBackend}` : arg);
|
|
89
109
|
const settings = {
|
|
90
110
|
headless: poolConfig.headless || 'shell',
|
|
91
|
-
args
|
|
111
|
+
args,
|
|
92
112
|
handleSIGINT: false, // ★ 기본 핸들러 해제
|
|
93
113
|
handleSIGTERM: false,
|
|
94
114
|
handleSIGHUP: false
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-factory.js","sourceRoot":"","sources":["../../../server/utils/headless-pool/browser-factory.ts"],"names":[],"mappings":"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","sourcesContent":["import { config, logger } from '@things-factory/env'\nimport { HeadlessPoolConfig } from './config'\n\n// Dynamic puppeteer loading with error handling\nlet puppeteer: any\ntry {\n puppeteer = require('puppeteer')\n} catch (err) {\n logger.warn('Puppeteer not available:', err)\n}\n\n/**\n * Browser Factory for creating and managing browser instances\n */\nexport class BrowserFactory {\n /**\n * Create a new browser instance\n */\n static async createBrowser(poolConfig: HeadlessPoolConfig): Promise<any> {\n if (!puppeteer) {\n throw new Error('Puppeteer is not available')\n }\n\n const launchSettings = this.getLaunchSettings(poolConfig)\n\n try {\n const browser = await puppeteer.launch(launchSettings)\n logger.info(`Browser instance created with headless: ${launchSettings.headless}`)\n return browser\n } catch (error) {\n logger.error('Failed to create browser instance:', error)\n throw error\n }\n }\n\n /**\n * Validate browser instance\n */\n static validateBrowser(browser: any): Promise<boolean> {\n return Promise.race([\n new Promise<boolean>(resolve => setTimeout(() => resolve(false), 1500)),\n browser\n .version()\n .then(() => true)\n .catch(() => false)\n ])\n }\n\n /**\n * Safely destroy browser instance with multiple cleanup strategies\n */\n static async destroyBrowser(browser: any): Promise<void> {\n const timeout = 5000 // 5 second timeout\n\n try {\n await Promise.race([\n this.gracefulDestroy(browser),\n new Promise((_, reject) => setTimeout(() => reject(new Error('Browser destruction timeout')), timeout))\n ])\n\n logger.info('🗑️ Browser instance destroyed successfully')\n } catch (error) {\n logger.warn('⚠️ Error destroying browser instance:', error)\n\n // Force kill as last resort\n await this.forceKillProcess(browser)\n }\n }\n\n private static async gracefulDestroy(browser: any): Promise<void> {\n // Step 1: Close all pages\n await this.closeAllPages(browser)\n\n // Step 2: Standard close\n await browser.close()\n\n // Step 3: Kill browser process if still running\n await this.killBrowserProcess(browser)\n }\n\n /**\n * Create browser with custom setup (for special cases like label pool)\n */\n static async createBrowserWithSetup(\n poolConfig: HeadlessPoolConfig,\n customSetup: (browser: any) => Promise<any>\n ): Promise<any> {\n const browser = await this.createBrowser(poolConfig)\n\n try {\n const result = await customSetup(browser)\n return result\n } catch (error) {\n // If setup fails, cleanup the browser\n await this.destroyBrowser(browser)\n throw error\n }\n }\n\n private static getLaunchSettings(poolConfig: HeadlessPoolConfig) {\n const settings: any = {\n headless: poolConfig.headless || 'shell',\n args: [...(poolConfig.args || [])],\n handleSIGINT: false, // ★ 기본 핸들러 해제\n handleSIGTERM: false,\n handleSIGHUP: false\n }\n\n // Add executable path if specified in config or environment\n const executablePath = poolConfig.executablePath || config.get('CHROMIUM_PATH')\n if (executablePath) {\n settings.executablePath = executablePath\n }\n\n return settings\n }\n\n private static async closeAllPages(browser: any): Promise<void> {\n try {\n const pages = await browser.pages()\n await Promise.all(pages.map((page: any) => page.close().catch(() => {})))\n } catch (error) {\n logger.warn('Failed to close pages:', error)\n }\n }\n\n private static async killBrowserProcess(browser: any): Promise<void> {\n try {\n const process = browser.process()\n if (process && !process.killed) {\n // Try graceful termination first\n process.kill('SIGTERM')\n\n // Wait a bit, then force kill if still alive\n await new Promise(resolve => setTimeout(resolve, 1000))\n\n if (!process.killed) {\n process.kill('SIGKILL')\n logger.info('🔪 Browser process force killed')\n }\n }\n } catch (error) {\n logger.warn('Failed to kill browser process:', error)\n }\n }\n\n private static async forceKillProcess(browser: any): Promise<void> {\n try {\n const process = browser.process()\n if (process && !process.killed) {\n process.kill('SIGKILL')\n logger.info('💀 Browser process force killed')\n }\n } catch (killError) {\n logger.error('💀 Failed to force kill browser process:', killError)\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"browser-factory.js","sourceRoot":"","sources":["../../../server/utils/headless-pool/browser-factory.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from 'fs'\nimport { config, logger } from '@things-factory/env'\nimport { HeadlessPoolConfig } from './config'\n\n// Dynamic puppeteer loading with error handling\nlet puppeteer: any\ntry {\n puppeteer = require('puppeteer')\n} catch (err) {\n logger.warn('Puppeteer not available:', err)\n}\n\n// Detect the best GL backend for the current environment (cached)\nlet detectedGLBackend: string | null = null\n\nfunction detectGLBackend(): string {\n if (detectedGLBackend !== null) return detectedGLBackend\n\n if (process.platform === 'darwin') {\n detectedGLBackend = 'angle'\n } else if (existsSync('/dev/nvidia0') || existsSync('/dev/dri/renderD128')) {\n detectedGLBackend = 'egl'\n } else {\n detectedGLBackend = 'swiftshader'\n }\n\n logger.info(`GL backend detected: ${detectedGLBackend}`)\n return detectedGLBackend\n}\n\n/**\n * Browser Factory for creating and managing browser instances\n */\nexport class BrowserFactory {\n /**\n * Create a new browser instance\n */\n static async createBrowser(poolConfig: HeadlessPoolConfig): Promise<any> {\n if (!puppeteer) {\n throw new Error('Puppeteer is not available')\n }\n\n const launchSettings = this.getLaunchSettings(poolConfig)\n\n try {\n const browser = await puppeteer.launch(launchSettings)\n logger.info(`Browser instance created with headless: ${launchSettings.headless}`)\n return browser\n } catch (error) {\n logger.error('Failed to create browser instance:', error)\n throw error\n }\n }\n\n /**\n * Validate browser instance\n */\n static validateBrowser(browser: any): Promise<boolean> {\n return Promise.race([\n new Promise<boolean>(resolve => setTimeout(() => resolve(false), 1500)),\n browser\n .version()\n .then(() => true)\n .catch(() => false)\n ])\n }\n\n /**\n * Safely destroy browser instance with multiple cleanup strategies\n */\n static async destroyBrowser(browser: any): Promise<void> {\n const timeout = 5000 // 5 second timeout\n\n try {\n await Promise.race([\n this.gracefulDestroy(browser),\n new Promise((_, reject) => setTimeout(() => reject(new Error('Browser destruction timeout')), timeout))\n ])\n\n logger.info('🗑️ Browser instance destroyed successfully')\n } catch (error) {\n logger.warn('⚠️ Error destroying browser instance:', error)\n\n // Force kill as last resort\n await this.forceKillProcess(browser)\n }\n }\n\n private static async gracefulDestroy(browser: any): Promise<void> {\n // Step 1: Close all pages\n await this.closeAllPages(browser)\n\n // Step 2: Standard close\n await browser.close()\n\n // Step 3: Kill browser process if still running\n await this.killBrowserProcess(browser)\n }\n\n /**\n * Create browser with custom setup (for special cases like label pool)\n */\n static async createBrowserWithSetup(\n poolConfig: HeadlessPoolConfig,\n customSetup: (browser: any) => Promise<any>\n ): Promise<any> {\n const browser = await this.createBrowser(poolConfig)\n\n try {\n const result = await customSetup(browser)\n return result\n } catch (error) {\n // If setup fails, cleanup the browser\n await this.destroyBrowser(browser)\n throw error\n }\n }\n\n private static getLaunchSettings(poolConfig: HeadlessPoolConfig) {\n const glBackend = detectGLBackend()\n const args = (poolConfig.args || []).map(arg =>\n arg.startsWith('--use-gl=') ? `--use-gl=${glBackend}` : arg\n )\n\n const settings: any = {\n headless: poolConfig.headless || 'shell',\n args,\n handleSIGINT: false, // ★ 기본 핸들러 해제\n handleSIGTERM: false,\n handleSIGHUP: false\n }\n\n // Add executable path if specified in config or environment\n const executablePath = poolConfig.executablePath || config.get('CHROMIUM_PATH')\n if (executablePath) {\n settings.executablePath = executablePath\n }\n\n return settings\n }\n\n private static async closeAllPages(browser: any): Promise<void> {\n try {\n const pages = await browser.pages()\n await Promise.all(pages.map((page: any) => page.close().catch(() => {})))\n } catch (error) {\n logger.warn('Failed to close pages:', error)\n }\n }\n\n private static async killBrowserProcess(browser: any): Promise<void> {\n try {\n const process = browser.process()\n if (process && !process.killed) {\n // Try graceful termination first\n process.kill('SIGTERM')\n\n // Wait a bit, then force kill if still alive\n await new Promise(resolve => setTimeout(resolve, 1000))\n\n if (!process.killed) {\n process.kill('SIGKILL')\n logger.info('🔪 Browser process force killed')\n }\n }\n } catch (error) {\n logger.warn('Failed to kill browser process:', error)\n }\n }\n\n private static async forceKillProcess(browser: any): Promise<void> {\n try {\n const process = browser.process()\n if (process && !process.killed) {\n process.kill('SIGKILL')\n logger.info('💀 Browser process force killed')\n }\n } catch (killError) {\n logger.error('💀 Failed to force kill browser process:', killError)\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/shell",
|
|
3
|
-
"version": "10.0.0-beta.
|
|
3
|
+
"version": "10.0.0-beta.8",
|
|
4
4
|
"description": "Core module for framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"things-factory": "bin/things-factory",
|
|
@@ -124,5 +124,5 @@
|
|
|
124
124
|
"pg": "^8.7.3",
|
|
125
125
|
"sqlite3": "^5.0.8"
|
|
126
126
|
},
|
|
127
|
-
"gitHead": "
|
|
127
|
+
"gitHead": "6e394d3533a03384cbdbd8c43a277a1dfba815ac"
|
|
128
128
|
}
|