@fanboynz/network-scanner 1.0.35
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.
- package/.github/workflows/npm-publish.yml +33 -0
- package/JSONMANUAL.md +121 -0
- package/LICENSE +674 -0
- package/README.md +357 -0
- package/config.json +74 -0
- package/lib/browserexit.js +522 -0
- package/lib/browserhealth.js +308 -0
- package/lib/cloudflare.js +660 -0
- package/lib/colorize.js +168 -0
- package/lib/compare.js +159 -0
- package/lib/compress.js +129 -0
- package/lib/fingerprint.js +613 -0
- package/lib/flowproxy.js +274 -0
- package/lib/grep.js +348 -0
- package/lib/ignore_similar.js +237 -0
- package/lib/nettools.js +1200 -0
- package/lib/output.js +633 -0
- package/lib/redirect.js +384 -0
- package/lib/searchstring.js +561 -0
- package/lib/validate_rules.js +1107 -0
- package/nwss.1 +824 -0
- package/nwss.js +2488 -0
- package/package.json +45 -0
- package/regex-samples.md +27 -0
- package/scanner-script-org.js +588 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser exit and cleanup handler module
|
|
3
|
+
* Provides graceful and forced browser closure functionality with comprehensive temp file cleanup
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Constants for temp file cleanup
|
|
7
|
+
const CHROME_TEMP_PATHS = [
|
|
8
|
+
'/tmp',
|
|
9
|
+
'/dev/shm',
|
|
10
|
+
'/tmp/snap-private-tmp/snap.chromium/tmp'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const CHROME_TEMP_PATTERNS = [
|
|
14
|
+
'.com.google.Chrome.*', // Google Chrome temp files
|
|
15
|
+
'.org.chromium.Chromium.*',
|
|
16
|
+
'puppeteer-*',
|
|
17
|
+
'.com.google.Chrome.*' // Ensure Google Chrome pattern is included
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Clean Chrome temporary files and directories
|
|
22
|
+
* @param {Object} options - Cleanup options
|
|
23
|
+
* @param {boolean} options.includeSnapTemp - Whether to clean snap temp directories
|
|
24
|
+
* @param {boolean} options.forceDebug - Whether to output debug logs
|
|
25
|
+
* @param {boolean} options.comprehensive - Whether to perform comprehensive cleanup of all temp locations
|
|
26
|
+
* @returns {Promise<Object>} Cleanup results
|
|
27
|
+
*/
|
|
28
|
+
async function cleanupChromeTempFiles(options = {}) {
|
|
29
|
+
const {
|
|
30
|
+
includeSnapTemp = false,
|
|
31
|
+
forceDebug = false,
|
|
32
|
+
comprehensive = false
|
|
33
|
+
} = options;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const { execSync } = require('child_process');
|
|
37
|
+
|
|
38
|
+
// Base cleanup commands for standard temp directories
|
|
39
|
+
const cleanupCommands = [
|
|
40
|
+
'rm -rf /tmp/.com.google.Chrome.* 2>/dev/null || true',
|
|
41
|
+
'rm -rf /tmp/.org.chromium.Chromium.* 2>/dev/null || true',
|
|
42
|
+
'rm -rf /tmp/puppeteer-* 2>/dev/null || true',
|
|
43
|
+
'rm -rf /dev/shm/.com.google.Chrome.* 2>/dev/null || true',
|
|
44
|
+
'rm -rf /dev/shm/.org.chromium.Chromium.* 2>/dev/null || true'
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// Add snap-specific cleanup if requested
|
|
48
|
+
if (includeSnapTemp || comprehensive) {
|
|
49
|
+
cleanupCommands.push(
|
|
50
|
+
'rm -rf /tmp/snap-private-tmp/snap.chromium/tmp/.org.chromium.Chromium.* 2>/dev/null || true',
|
|
51
|
+
'rm -rf /tmp/snap-private-tmp/snap.chromium/tmp/puppeteer-* 2>/dev/null || true'
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let totalCleaned = 0;
|
|
56
|
+
|
|
57
|
+
for (const command of cleanupCommands) {
|
|
58
|
+
try {
|
|
59
|
+
// Get file count before cleanup for reporting
|
|
60
|
+
const listCommand = command.replace('rm -rf', 'ls -1d').replace(' 2>/dev/null || true', ' 2>/dev/null | wc -l || echo 0');
|
|
61
|
+
const fileCount = parseInt(execSync(listCommand, { stdio: 'pipe' }).toString().trim()) || 0;
|
|
62
|
+
|
|
63
|
+
if (fileCount > 0) {
|
|
64
|
+
execSync(command, { stdio: 'ignore' });
|
|
65
|
+
totalCleaned += fileCount;
|
|
66
|
+
|
|
67
|
+
if (forceDebug) {
|
|
68
|
+
const pathPattern = command.match(/rm -rf ([^ ]+)/)?.[1] || 'unknown';
|
|
69
|
+
console.log(`[debug] [temp-cleanup] Cleaned ${fileCount} items from ${pathPattern}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch (cmdErr) {
|
|
73
|
+
// Ignore individual command errors but log in debug mode
|
|
74
|
+
if (forceDebug) {
|
|
75
|
+
console.log(`[debug] [temp-cleanup] Cleanup command failed: ${command} (${cmdErr.message})`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (forceDebug) {
|
|
81
|
+
console.log(`[debug] [temp-cleanup] Standard cleanup completed (${totalCleaned} items)`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { success: true, itemsCleaned: totalCleaned };
|
|
85
|
+
|
|
86
|
+
} catch (cleanupErr) {
|
|
87
|
+
if (forceDebug) {
|
|
88
|
+
console.log(`[debug] [temp-cleanup] Chrome cleanup error: ${cleanupErr.message}`);
|
|
89
|
+
}
|
|
90
|
+
return { success: false, error: cleanupErr.message, itemsCleaned: 0 };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Comprehensive temp file cleanup that systematically checks all known Chrome temp locations
|
|
96
|
+
* @param {Object} options - Cleanup options
|
|
97
|
+
* @param {boolean} options.forceDebug - Whether to output debug logs
|
|
98
|
+
* @param {boolean} options.verbose - Whether to show verbose output
|
|
99
|
+
* @returns {Promise<Object>} Cleanup results
|
|
100
|
+
*/
|
|
101
|
+
async function comprehensiveChromeTempCleanup(options = {}) {
|
|
102
|
+
const { forceDebug = false, verbose = false } = options;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const { execSync } = require('child_process');
|
|
106
|
+
let totalCleaned = 0;
|
|
107
|
+
|
|
108
|
+
if (verbose && !forceDebug) {
|
|
109
|
+
console.log(`[temp-cleanup] Scanning Chrome/Puppeteer temporary files...`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const basePath of CHROME_TEMP_PATHS) {
|
|
113
|
+
// Check if the base path exists before trying to clean it
|
|
114
|
+
try {
|
|
115
|
+
const pathExists = execSync(`test -d "${basePath}" && echo "exists" || echo "missing"`, { stdio: 'pipe' })
|
|
116
|
+
.toString().trim() === 'exists';
|
|
117
|
+
|
|
118
|
+
if (!pathExists) {
|
|
119
|
+
if (forceDebug) {
|
|
120
|
+
console.log(`[debug] [temp-cleanup] Skipping non-existent path: ${basePath}`);
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const pattern of CHROME_TEMP_PATTERNS) {
|
|
126
|
+
const fullPattern = `${basePath}/${pattern}`;
|
|
127
|
+
|
|
128
|
+
// Count items before deletion
|
|
129
|
+
const countCommand = `ls -1d ${fullPattern} 2>/dev/null | wc -l || echo 0`;
|
|
130
|
+
const itemCount = parseInt(execSync(countCommand, { stdio: 'pipe' }).toString().trim()) || 0;
|
|
131
|
+
|
|
132
|
+
if (itemCount > 0) {
|
|
133
|
+
const deleteCommand = `rm -rf ${fullPattern} 2>/dev/null || true`;
|
|
134
|
+
execSync(deleteCommand, { stdio: 'ignore' });
|
|
135
|
+
totalCleaned += itemCount;
|
|
136
|
+
|
|
137
|
+
if (forceDebug) {
|
|
138
|
+
console.log(`[debug] [temp-cleanup] Removed ${itemCount} items matching ${fullPattern}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch (pathErr) {
|
|
143
|
+
if (forceDebug) {
|
|
144
|
+
console.log(`[debug] [temp-cleanup] Error checking path ${basePath}: ${pathErr.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (verbose && totalCleaned > 0) {
|
|
150
|
+
console.log(`[temp-cleanup] ? Removed ${totalCleaned} temporary file(s)/folder(s)`);
|
|
151
|
+
} else if (verbose && totalCleaned === 0) {
|
|
152
|
+
console.log(`[temp-cleanup] ?? No temporary files found to remove`);
|
|
153
|
+
} else if (forceDebug) {
|
|
154
|
+
console.log(`[debug] [temp-cleanup] Comprehensive cleanup completed (${totalCleaned} items)`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { success: true, itemsCleaned: totalCleaned };
|
|
158
|
+
|
|
159
|
+
} catch (err) {
|
|
160
|
+
const errorMsg = `Comprehensive temp file cleanup failed: ${err.message}`;
|
|
161
|
+
if (verbose) {
|
|
162
|
+
console.warn(`[temp-cleanup] ? ${errorMsg}`);
|
|
163
|
+
} else if (forceDebug) {
|
|
164
|
+
console.log(`[debug] [temp-cleanup] ${errorMsg}`);
|
|
165
|
+
}
|
|
166
|
+
return { success: false, error: err.message, itemsCleaned: 0 };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Cleanup specific user data directory (for browser instances)
|
|
172
|
+
* @param {string} userDataDir - Path to user data directory to clean
|
|
173
|
+
* @param {boolean} forceDebug - Whether to output debug logs
|
|
174
|
+
* @returns {Promise<Object>} Cleanup results
|
|
175
|
+
*/
|
|
176
|
+
async function cleanupUserDataDir(userDataDir, forceDebug = false) {
|
|
177
|
+
if (!userDataDir) {
|
|
178
|
+
return { success: true, cleaned: false, reason: 'No user data directory specified' };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const fs = require('fs');
|
|
183
|
+
|
|
184
|
+
if (!fs.existsSync(userDataDir)) {
|
|
185
|
+
if (forceDebug) {
|
|
186
|
+
console.log(`[debug] [user-data] User data directory does not exist: ${userDataDir}`);
|
|
187
|
+
}
|
|
188
|
+
return { success: true, cleaned: false, reason: 'Directory does not exist' };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
fs.rmSync(userDataDir, { recursive: true, force: true });
|
|
192
|
+
|
|
193
|
+
if (forceDebug) {
|
|
194
|
+
console.log(`[debug] [user-data] Cleaned user data directory: ${userDataDir}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { success: true, cleaned: true };
|
|
198
|
+
|
|
199
|
+
} catch (rmErr) {
|
|
200
|
+
if (forceDebug) {
|
|
201
|
+
console.log(`[debug] [user-data] Failed to remove user data directory ${userDataDir}: ${rmErr.message}`);
|
|
202
|
+
}
|
|
203
|
+
return { success: false, error: rmErr.message, cleaned: false };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Attempts to gracefully close all browser pages and the browser instance
|
|
209
|
+
* @param {import('puppeteer').Browser} browser - The Puppeteer browser instance
|
|
210
|
+
* @param {boolean} forceDebug - Whether to output debug logs
|
|
211
|
+
* @returns {Promise<void>}
|
|
212
|
+
*/
|
|
213
|
+
async function gracefulBrowserCleanup(browser, forceDebug = false) {
|
|
214
|
+
if (forceDebug) console.log(`[debug] [browser] Getting all browser pages...`);
|
|
215
|
+
const pages = await browser.pages();
|
|
216
|
+
if (forceDebug) console.log(`[debug] [browser] Found ${pages.length} pages to close`);
|
|
217
|
+
|
|
218
|
+
await Promise.all(pages.map(async (page) => {
|
|
219
|
+
if (!page.isClosed()) {
|
|
220
|
+
try {
|
|
221
|
+
if (forceDebug) console.log(`[debug] [browser] Closing page: ${page.url()}`);
|
|
222
|
+
await page.close();
|
|
223
|
+
if (forceDebug) console.log(`[debug] [browser] Page closed successfully`);
|
|
224
|
+
} catch (err) {
|
|
225
|
+
// Force close if normal close fails
|
|
226
|
+
if (forceDebug) console.log(`[debug] [browser] Force closing page: ${err.message}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}));
|
|
230
|
+
|
|
231
|
+
if (forceDebug) console.log(`[debug] [browser] All pages closed, closing browser...`);
|
|
232
|
+
await browser.close();
|
|
233
|
+
if (forceDebug) console.log(`[debug] [browser] Browser closed successfully`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Force kills the browser process using system signals
|
|
238
|
+
* @param {import('puppeteer').Browser} browser - The Puppeteer browser instance
|
|
239
|
+
* @param {boolean} forceDebug - Whether to output debug logs
|
|
240
|
+
* @returns {Promise<void>}
|
|
241
|
+
*/
|
|
242
|
+
async function forceBrowserKill(browser, forceDebug = false) {
|
|
243
|
+
try {
|
|
244
|
+
if (forceDebug) console.log(`[debug] [browser] Attempting force closure of browser process...`);
|
|
245
|
+
|
|
246
|
+
const browserProcess = browser.process();
|
|
247
|
+
if (!browserProcess || !browserProcess.pid) {
|
|
248
|
+
if (forceDebug) console.log(`[debug] [browser] No browser process available`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const mainPid = browserProcess.pid;
|
|
253
|
+
if (forceDebug) console.log(`[debug] [browser] Main Chrome PID: ${mainPid}`);
|
|
254
|
+
|
|
255
|
+
// Find and kill ALL related Chrome processes
|
|
256
|
+
const { execSync } = require('child_process');
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
// Find all Chrome processes with puppeteer in command line
|
|
261
|
+
const psCmd = `ps -eo pid,cmd | grep "puppeteer.*chrome" | grep -v grep`;
|
|
262
|
+
const psOutput = execSync(psCmd, { encoding: 'utf8', timeout: 5000 });
|
|
263
|
+
const lines = psOutput.trim().split('\n').filter(line => line.trim());
|
|
264
|
+
|
|
265
|
+
const pidsToKill = [];
|
|
266
|
+
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
const match = line.trim().match(/^\s*(\d+)/);
|
|
269
|
+
if (match) {
|
|
270
|
+
const pid = parseInt(match[1]);
|
|
271
|
+
if (!isNaN(pid)) {
|
|
272
|
+
pidsToKill.push(pid);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (forceDebug) {
|
|
278
|
+
console.log(`[debug] [browser] Found ${pidsToKill.length} Chrome processes to kill: [${pidsToKill.join(', ')}]`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Kill all processes with SIGTERM first (graceful)
|
|
282
|
+
for (const pid of pidsToKill) {
|
|
283
|
+
try {
|
|
284
|
+
process.kill(pid, 'SIGTERM');
|
|
285
|
+
if (forceDebug) console.log(`[debug] [browser] Sent SIGTERM to PID ${pid}`);
|
|
286
|
+
} catch (killErr) {
|
|
287
|
+
if (forceDebug) console.log(`[debug] [browser] Failed to send SIGTERM to PID ${pid}: ${killErr.message}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Wait for graceful termination
|
|
292
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
293
|
+
|
|
294
|
+
// Force kill any remaining processes with SIGKILL
|
|
295
|
+
for (const pid of pidsToKill) {
|
|
296
|
+
try {
|
|
297
|
+
// Check if process still exists using signal 0
|
|
298
|
+
process.kill(pid, 0);
|
|
299
|
+
// If we reach here, process still exists - force kill it
|
|
300
|
+
process.kill(pid, 'SIGKILL');
|
|
301
|
+
if (forceDebug) console.log(`[debug] [browser] Force killed PID ${pid} with SIGKILL`);
|
|
302
|
+
} catch (checkErr) {
|
|
303
|
+
// Process already dead (ESRCH error is expected and good)
|
|
304
|
+
if (forceDebug && checkErr.code !== 'ESRCH') {
|
|
305
|
+
console.log(`[debug] [browser] Error checking/killing PID ${pid}: ${checkErr.message}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Final verification - check if any processes are still alive
|
|
311
|
+
if (forceDebug) {
|
|
312
|
+
try {
|
|
313
|
+
const verifyCmd = `ps -eo pid,cmd | grep "puppeteer.*chrome" | grep -v grep | wc -l`;
|
|
314
|
+
const remainingCount = execSync(verifyCmd, { encoding: 'utf8', timeout: 2000 }).trim();
|
|
315
|
+
console.log(`[debug] [browser] Remaining Chrome processes after cleanup: ${remainingCount}`);
|
|
316
|
+
} catch (verifyErr) {
|
|
317
|
+
console.log(`[debug] [browser] Could not verify process cleanup: ${verifyErr.message}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
} catch (psErr) {
|
|
322
|
+
// Fallback to original method if ps command fails
|
|
323
|
+
if (forceDebug) console.log(`[debug] [browser] ps command failed, using fallback method: ${psErr.message}`);
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
browserProcess.kill('SIGTERM');
|
|
327
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
328
|
+
|
|
329
|
+
// Check if main process still exists and force kill if needed
|
|
330
|
+
try {
|
|
331
|
+
process.kill(mainPid, 0); // Check existence
|
|
332
|
+
browserProcess.kill('SIGKILL'); // Force kill if still alive
|
|
333
|
+
if (forceDebug) console.log(`[debug] [browser] Fallback: Force killed main PID ${mainPid}`);
|
|
334
|
+
} catch (checkErr) {
|
|
335
|
+
if (forceDebug && checkErr.code !== 'ESRCH') {
|
|
336
|
+
console.log(`[debug] [browser] Fallback check error for PID ${mainPid}: ${checkErr.message}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
} catch (fallbackErr) {
|
|
340
|
+
if (forceDebug) console.log(`[debug] [browser] Fallback kill failed: ${fallbackErr.message}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
} catch (forceKillErr) {
|
|
345
|
+
console.error(`[error] [browser] Failed to force kill browser: ${forceKillErr.message}`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
if (browser.isConnected()) {
|
|
350
|
+
browser.disconnect();
|
|
351
|
+
if (forceDebug) console.log(`[debug] [browser] Browser connection disconnected`);
|
|
352
|
+
}
|
|
353
|
+
} catch (disconnectErr) {
|
|
354
|
+
if (forceDebug) console.log(`[debug] [browser] Failed to disconnect browser: ${disconnectErr.message}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Kill all Chrome processes by command line pattern (nuclear option)
|
|
360
|
+
* @param {boolean} forceDebug - Whether to output debug logs
|
|
361
|
+
* @returns {Promise<void>}
|
|
362
|
+
*/
|
|
363
|
+
async function killAllPuppeteerChrome(forceDebug = false) {
|
|
364
|
+
try {
|
|
365
|
+
const { execSync } = require('child_process');
|
|
366
|
+
|
|
367
|
+
if (forceDebug) console.log(`[debug] [browser] Nuclear option: killing all puppeteer Chrome processes...`);
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
execSync(`pkill -f "puppeteer.*chrome"`, { stdio: 'ignore', timeout: 5000 });
|
|
371
|
+
if (forceDebug) console.log(`[debug] [browser] pkill completed`);
|
|
372
|
+
} catch (pkillErr) {
|
|
373
|
+
if (forceDebug && pkillErr.status !== 1) {
|
|
374
|
+
console.log(`[debug] [browser] pkill failed with status ${pkillErr.status}: ${pkillErr.message}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
379
|
+
|
|
380
|
+
} catch (nuclearErr) {
|
|
381
|
+
console.error(`[error] [browser] Nuclear Chrome kill failed: ${nuclearErr.message}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Handles comprehensive browser cleanup including processes, temp files, and user data
|
|
387
|
+
* @param {import('puppeteer').Browser} browser - The Puppeteer browser instance
|
|
388
|
+
* @param {Object} options - Cleanup options
|
|
389
|
+
* @param {boolean} options.forceDebug - Whether to output debug logs
|
|
390
|
+
* @param {number} options.timeout - Timeout in milliseconds before force closure (default: 10000)
|
|
391
|
+
* @param {boolean} options.exitOnFailure - Whether to exit process on cleanup failure (default: true)
|
|
392
|
+
* @param {boolean} options.cleanTempFiles - Whether to clean standard temp files (default: true)
|
|
393
|
+
* @param {boolean} options.comprehensiveCleanup - Whether to perform comprehensive temp file cleanup (default: false)
|
|
394
|
+
* @param {string} options.userDataDir - User data directory to clean (optional)
|
|
395
|
+
* @param {boolean} options.verbose - Whether to show verbose cleanup output (default: false)
|
|
396
|
+
* @returns {Promise<Object>} - Returns cleanup results object
|
|
397
|
+
*/
|
|
398
|
+
async function handleBrowserExit(browser, options = {}) {
|
|
399
|
+
const {
|
|
400
|
+
forceDebug = false,
|
|
401
|
+
timeout = 10000,
|
|
402
|
+
exitOnFailure = true,
|
|
403
|
+
cleanTempFiles = true,
|
|
404
|
+
comprehensiveCleanup = false,
|
|
405
|
+
userDataDir = null,
|
|
406
|
+
verbose = false
|
|
407
|
+
} = options;
|
|
408
|
+
|
|
409
|
+
if (forceDebug) console.log(`[debug] [browser] Starting comprehensive browser cleanup...`);
|
|
410
|
+
|
|
411
|
+
const results = {
|
|
412
|
+
browserClosed: false,
|
|
413
|
+
tempFilescleaned: 0,
|
|
414
|
+
userDataCleaned: false,
|
|
415
|
+
success: false,
|
|
416
|
+
errors: []
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
// Step 1: Browser process cleanup
|
|
421
|
+
try {
|
|
422
|
+
// Race cleanup against timeout
|
|
423
|
+
await Promise.race([
|
|
424
|
+
gracefulBrowserCleanup(browser, forceDebug),
|
|
425
|
+
new Promise((_, reject) =>
|
|
426
|
+
setTimeout(() => reject(new Error('Browser cleanup timeout')), timeout)
|
|
427
|
+
)
|
|
428
|
+
]);
|
|
429
|
+
|
|
430
|
+
results.browserClosed = true;
|
|
431
|
+
|
|
432
|
+
} catch (browserCloseErr) {
|
|
433
|
+
results.errors.push(`Browser cleanup failed: ${browserCloseErr.message}`);
|
|
434
|
+
|
|
435
|
+
if (forceDebug || verbose) {
|
|
436
|
+
console.warn(`[warn] [browser] Browser cleanup had issues: ${browserCloseErr.message}`);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Attempt force kill
|
|
440
|
+
await forceBrowserKill(browser, forceDebug);
|
|
441
|
+
|
|
442
|
+
// Nuclear option if force kill didn't work
|
|
443
|
+
if (forceDebug) console.log(`[debug] [browser] Attempting nuclear cleanup...`);
|
|
444
|
+
await killAllPuppeteerChrome(forceDebug);
|
|
445
|
+
|
|
446
|
+
results.browserClosed = true; // Assume success after nuclear option
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Step 2: User data directory cleanup
|
|
450
|
+
if (userDataDir) {
|
|
451
|
+
const userDataResult = await cleanupUserDataDir(userDataDir, forceDebug);
|
|
452
|
+
results.userDataCleaned = userDataResult.cleaned;
|
|
453
|
+
if (!userDataResult.success) {
|
|
454
|
+
results.errors.push(`User data cleanup failed: ${userDataResult.error}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Step 3: Temp file cleanup
|
|
459
|
+
if (cleanTempFiles) {
|
|
460
|
+
if (comprehensiveCleanup) {
|
|
461
|
+
const tempResult = await comprehensiveChromeTempCleanup({ forceDebug, verbose });
|
|
462
|
+
results.tempFilesCleanedSuccess = tempResult.success;
|
|
463
|
+
results.tempFilesCleanedComprehensive = true;
|
|
464
|
+
|
|
465
|
+
if (tempResult.success) {
|
|
466
|
+
results.tempFilesCleanedCount = tempResult.itemsCleaned;
|
|
467
|
+
} else {
|
|
468
|
+
results.errors.push(`Comprehensive temp cleanup failed: ${tempResult.error}`);
|
|
469
|
+
}
|
|
470
|
+
} else {
|
|
471
|
+
const tempResult = await cleanupChromeTempFiles({
|
|
472
|
+
includeSnapTemp: true,
|
|
473
|
+
forceDebug,
|
|
474
|
+
comprehensive: false
|
|
475
|
+
});
|
|
476
|
+
results.tempFilesCleanedSuccess = tempResult.success;
|
|
477
|
+
|
|
478
|
+
if (tempResult.success) {
|
|
479
|
+
results.tempFilesCleanedCount = tempResult.itemsCleaned;
|
|
480
|
+
} else {
|
|
481
|
+
results.errors.push(`Standard temp cleanup failed: ${tempResult.error}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Determine overall success
|
|
487
|
+
results.success = results.browserClosed &&
|
|
488
|
+
(results.errors.length === 0 || !exitOnFailure);
|
|
489
|
+
|
|
490
|
+
if (forceDebug) {
|
|
491
|
+
console.log(`[debug] [browser] Cleanup completed - Browser: ${results.browserClosed}, ` +
|
|
492
|
+
`Temp files: ${results.tempFilesCleanedCount || 0}, ` +
|
|
493
|
+
`User data: ${results.userDataCleaned}, ` +
|
|
494
|
+
`Errors: ${results.errors.length}`);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return results;
|
|
498
|
+
|
|
499
|
+
} catch (overallErr) {
|
|
500
|
+
results.errors.push(`Overall cleanup failed: ${overallErr.message}`);
|
|
501
|
+
results.success = false;
|
|
502
|
+
|
|
503
|
+
if (exitOnFailure) {
|
|
504
|
+
if (forceDebug) console.log(`[debug] [browser] Forcing process exit due to cleanup failure`);
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return results;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
module.exports = {
|
|
513
|
+
handleBrowserExit,
|
|
514
|
+
gracefulBrowserCleanup,
|
|
515
|
+
forceBrowserKill,
|
|
516
|
+
killAllPuppeteerChrome,
|
|
517
|
+
cleanupChromeTempFiles,
|
|
518
|
+
comprehensiveChromeTempCleanup,
|
|
519
|
+
cleanupUserDataDir,
|
|
520
|
+
CHROME_TEMP_PATHS,
|
|
521
|
+
CHROME_TEMP_PATTERNS
|
|
522
|
+
};
|