@fanboynz/network-scanner 1.0.62 → 1.0.64

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.
@@ -471,6 +471,25 @@ async function interactWithElements(page, options = {}) {
471
471
  } = options;
472
472
 
473
473
  try {
474
+ // Ensure page is in valid state for element interaction
475
+ try {
476
+ // Check if page is closed before attempting interaction
477
+ if (page.isClosed()) {
478
+ if (options.forceDebug) {
479
+ console.log(`[interaction] Page is closed, skipping element interaction`);
480
+ }
481
+ return;
482
+ }
483
+
484
+ // Shorter timeout for element interaction since body should already exist
485
+ await page.waitForSelector('body', { timeout: 2000 });
486
+ } catch (bodyWaitErr) {
487
+ if (options.forceDebug) {
488
+ console.log(`[interaction] Page not ready for element interaction: ${bodyWaitErr.message}`);
489
+ }
490
+ return;
491
+ }
492
+
474
493
  // Get viewport dimensions for coordinate bounds
475
494
  const viewport = await page.viewport();
476
495
  const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
@@ -589,6 +608,13 @@ async function simulateTyping(page, text, options = {}) {
589
608
  } = options;
590
609
 
591
610
  try {
611
+ // Ensure page is ready for typing
612
+ try {
613
+ await page.waitForSelector('body', { timeout: 1000 });
614
+ } catch (bodyWaitErr) {
615
+ return; // Silently skip typing if page not ready
616
+ }
617
+
592
618
  for (let i = 0; i < text.length; i++) {
593
619
  const char = text[i];
594
620
 
@@ -683,6 +709,42 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
683
709
  } = options;
684
710
 
685
711
  try {
712
+ // Validate page state before starting interaction
713
+ try {
714
+ // Use site-specific timeout based on configuration
715
+ // Sites with longer configured timeouts get more time for body detection
716
+ const siteTimeout = options.siteTimeout || 30000; // From site config
717
+ const bodyTimeout = Math.min(Math.max(siteTimeout / 6, 3000), 8000); // 3-8 seconds
718
+
719
+ await page.waitForSelector('body', { timeout: bodyTimeout });
720
+
721
+ // Additional check: ensure page is not closed/crashed
722
+ if (page.isClosed()) {
723
+ if (forceDebug) {
724
+ console.log(`[interaction] Page is closed for ${currentUrl}, skipping interaction`);
725
+ }
726
+ return;
727
+ }
728
+
729
+ const bodyExists = await page.$('body');
730
+ if (!bodyExists) {
731
+ if (forceDebug) {
732
+ console.log(`[interaction] Body element not found for ${currentUrl}, skipping interaction`);
733
+ }
734
+ return;
735
+ }
736
+ } catch (bodyCheckErr) {
737
+ if (forceDebug) {
738
+ console.log(`[interaction] Page not ready for interaction on ${currentUrl} (waited ${bodyTimeout}ms): ${bodyCheckErr.message}`);
739
+ // For very slow sites, we might want to try a minimal interaction anyway
740
+ if (bodyTimeout >= 6000 && !bodyCheckErr.message.includes('closed')) {
741
+ console.log(`[interaction] Attempting minimal mouse movement only for slow-loading ${currentUrl}`);
742
+ return await performMinimalInteraction(page, currentUrl, options, forceDebug);
743
+ }
744
+ }
745
+ return;
746
+ }
747
+
686
748
  // Get viewport dimensions
687
749
  const viewport = await page.viewport();
688
750
  const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
@@ -753,7 +815,16 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
753
815
  // Final hover position
754
816
  const finalPos = generateRandomCoordinates(maxX, maxY);
755
817
  await humanLikeMouseMove(page, currentPos.x, currentPos.y, finalPos.x, finalPos.y);
756
- await page.hover('body');
818
+
819
+ // Safe hover with validation
820
+ try {
821
+ const bodyElement = await page.$('body');
822
+ if (bodyElement) {
823
+ await page.hover('body');
824
+ }
825
+ } catch (hoverErr) {
826
+ // Silently handle hover failures - not critical
827
+ }
757
828
 
758
829
  const elapsedTime = Date.now() - startTime;
759
830
  if (forceDebug) {
@@ -768,6 +839,35 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
768
839
  }
769
840
  }
770
841
 
842
+ /**
843
+ * Performs minimal interaction for very slow or problematic pages
844
+ * Only does basic mouse movement without body validation
845
+ */
846
+ async function performMinimalInteraction(page, currentUrl, options = {}, forceDebug = false) {
847
+ try {
848
+ if (page.isClosed()) return;
849
+
850
+ const viewport = await page.viewport();
851
+ const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
852
+ const maxY = viewport ? viewport.height : DEFAULT_VIEWPORT.HEIGHT;
853
+
854
+ if (forceDebug) {
855
+ console.log(`[interaction] Performing minimal interaction for slow page: ${new URL(currentUrl).hostname}`);
856
+ }
857
+
858
+ // Just do basic mouse movement without body-dependent operations
859
+ const startPos = generateRandomCoordinates(maxX, maxY);
860
+ const endPos = generateRandomCoordinates(maxX, maxY);
861
+
862
+ await page.mouse.move(startPos.x, startPos.y);
863
+ await fastTimeout(500);
864
+ await humanLikeMouseMove(page, startPos.x, startPos.y, endPos.x, endPos.y);
865
+
866
+ } catch (minimalErr) {
867
+ // Even minimal interaction failed - page is truly broken
868
+ }
869
+ }
870
+
771
871
  /**
772
872
  * Creates an optimized interaction configuration based on site characteristics
773
873
  *
package/nwss.js CHANGED
@@ -1,4 +1,4 @@
1
- // === Network scanner script (nwss.js) v1.0.61 ===
1
+ // === Network scanner script (nwss.js) v1.0.64 ===
2
2
 
3
3
  // puppeteer for browser automation, fs for file system operations, psl for domain parsing.
4
4
  // const pLimit = require('p-limit'); // Will be dynamically imported
@@ -42,13 +42,50 @@ function fastTimeout(ms) {
42
42
  return new Promise(resolve => setTimeout(resolve, ms));
43
43
  }
44
44
 
45
+ /**
46
+ * Detects the installed Puppeteer version dynamically
47
+ * @returns {Object} Version info and compatibility settings
48
+ */
49
+ function detectPuppeteerVersion() {
50
+ try {
51
+ const puppeteer = require('puppeteer');
52
+ let versionString = null;
53
+
54
+ // Try multiple methods to get version
55
+ if (puppeteer.version) {
56
+ versionString = puppeteer.version;
57
+ } else if (puppeteer._version) {
58
+ versionString = puppeteer._version;
59
+ } else {
60
+ // Fallback: try to get from Browser.version() after launch
61
+ return { majorVersion: 22, useShellMode: true, detected: false };
62
+ }
63
+
64
+ const majorVersion = parseInt(versionString.split('.')[0]);
65
+ const useShellMode = majorVersion >= 22;
66
+
67
+ return {
68
+ version: versionString,
69
+ majorVersion,
70
+ useShellMode,
71
+ detected: true
72
+ };
73
+ } catch (err) {
74
+ if (forceDebug) {
75
+ console.log(formatLogMessage('debug', `Could not detect Puppeteer version: ${err.message}`));
76
+ }
77
+ // Safe fallback - assume newer version
78
+ return { majorVersion: 22, useShellMode: true, detected: false };
79
+ }
80
+ }
81
+
45
82
  // Enhanced redirect handling
46
83
  const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/redirect');
47
84
  // Ensure web browser is working correctly
48
85
  const { monitorBrowserHealth, isBrowserHealthy } = require('./lib/browserhealth');
49
86
 
50
87
  // --- Script Configuration & Constants ---
51
- const VERSION = '1.0.61'; // Script version
88
+ const VERSION = '1.0.64'; // Script version
52
89
 
53
90
  // get startTime
54
91
  const startTime = Date.now();
@@ -916,6 +953,23 @@ function matchesIgnoreDomain(domain, ignorePatterns) {
916
953
  function setupFrameHandling(page, forceDebug) {
917
954
  // Handle frame creation with error suppression
918
955
  page.on('frameattached', async (frame) => {
956
+ // Enhanced frame handling for Puppeteer 23.x
957
+ try {
958
+ // Check if frame is valid before processing
959
+ if (frame.isDetached()) {
960
+ if (forceDebug) {
961
+ console.log(formatLogMessage('debug', `Skipping detached frame`));
962
+ }
963
+ return;
964
+ }
965
+ } catch (detachError) {
966
+ // Frame state checking can throw in 23.x, handle gracefully
967
+ if (forceDebug) {
968
+ console.log(formatLogMessage('debug', `Frame state check failed: ${detachError.message}`));
969
+ }
970
+ return;
971
+ }
972
+
919
973
  if (frame.parentFrame()) { // Only handle child frames, not main frame
920
974
  try {
921
975
  const frameUrl = frame.url();
@@ -1024,6 +1078,23 @@ function setupFrameHandling(page, forceDebug) {
1024
1078
  userDataDir = tempUserDataDir; // Store for cleanup tracking (use outer scope variable)
1025
1079
 
1026
1080
  // Try to find system Chrome installation to avoid Puppeteer downloads
1081
+
1082
+ // Detect Puppeteer version for headless mode compatibility
1083
+ let headlessMode = launchHeadless;
1084
+ if (launchHeadless) {
1085
+ const puppeteerInfo = detectPuppeteerVersion();
1086
+
1087
+ if (puppeteerInfo.useShellMode) {
1088
+ headlessMode = 'shell'; // Use fast chrome-headless-shell for 22.x+
1089
+ if (forceDebug) console.log(formatLogMessage('debug', `Using chrome-headless-shell (Puppeteer ${puppeteerInfo.version || 'v' + puppeteerInfo.majorVersion + '.x'})`));
1090
+ } else {
1091
+ headlessMode = true; // Use regular headless for older versions
1092
+ if (forceDebug) console.log(formatLogMessage('debug', 'Could not detect Puppeteer version, using regular headless mode'));
1093
+ }
1094
+ } else {
1095
+ headlessMode = false; // Headful mode
1096
+ }
1097
+
1027
1098
  const systemChromePaths = [
1028
1099
  '/usr/bin/google-chrome-stable',
1029
1100
  '/usr/bin/google-chrome',
@@ -1048,28 +1119,24 @@ function setupFrameHandling(page, forceDebug) {
1048
1119
  // Force temporary user data directory for complete cleanup control
1049
1120
  userDataDir: tempUserDataDir,
1050
1121
  // Puppeteer 22.x headless mode optimization
1051
- // 'shell' = chrome-headless-shell (performance optimized)
1052
- // true = new unified headless mode (full Chrome features)
1053
- // false = headful mode (GUI visible)
1054
- headless: launchHeadless ? 'shell' : false, // Use shell for maximum performance
1122
+ // Auto-detect best headless mode based on Puppeteer version
1123
+ headless: headlessMode,
1055
1124
  args: [
1056
1125
  // Disk space controls - 50MB cache limits
1126
+ '--disable-features=VizDisplayCompositor',
1057
1127
  '--disk-cache-size=52428800', // 50MB disk cache (50 * 1024 * 1024)
1058
1128
  '--media-cache-size=52428800', // 50MB media cache
1059
1129
  '--disable-application-cache',
1060
1130
  '--disable-offline-load-stale-cache',
1061
1131
  '--disable-background-downloads',
1062
- // PERFORMANCE: Additional Puppeteer 22.x optimizations
1063
- '--disable-features=VizDisplayCompositor,AudioServiceOutOfProcess',
1132
+ // PERFORMANCE: Enhanced Puppeteer 23.x optimizations
1133
+ '--disable-features=AudioServiceOutOfProcess,VizDisplayCompositor',
1134
+ '--disable-features=TranslateUI,BlinkGenPropertyTrees,Translate',
1135
+ '--disable-features=BackForwardCache,AcceptCHFrame',
1064
1136
  '--disable-ipc-flooding-protection',
1065
- '--disable-renderer-backgrounding',
1066
- '--disable-backgrounding-occluded-windows',
1067
- '--disable-features=TranslateUI,BlinkGenPropertyTrees',
1068
- '--disable-features=Translate,BackForwardCache,AcceptCHFrame',
1069
1137
  '--aggressive-cache-discard',
1070
1138
  '--memory-pressure-off',
1071
1139
  '--max_old_space_size=2048',
1072
- '--disable-features=VizDisplayCompositor',
1073
1140
  '--no-first-run',
1074
1141
  '--disable-default-apps',
1075
1142
  '--disable-component-extensions-with-background-pages',
@@ -1094,13 +1161,19 @@ function setupFrameHandling(page, forceDebug) {
1094
1161
  '--disable-web-security',
1095
1162
  '--allow-running-insecure-content',
1096
1163
  '--disable-features=HttpsFirstBalancedModeAutoEnable',
1097
- '--run-all-compositor-stages-before-draw',
1098
- '--disable-threaded-animation',
1099
- '--disable-threaded-scrolling',
1100
- '--disable-checker-imaging',
1101
- '--disable-image-animation-resync'
1164
+ // Puppeteer 23.x: Enhanced performance and stability args
1165
+ '--disable-renderer-backgrounding',
1166
+ '--disable-backgrounding-occluded-windows',
1167
+ '--disable-background-timer-throttling',
1168
+ '--disable-features=site-per-process', // Better for single-site scanning
1169
+ '--disable-blink-features=AutomationControlled', // Avoid detection
1170
+ '--no-zygote', // Better process isolation
1102
1171
  ],
1103
- protocolTimeout: 60000 // 60 seconds
1172
+ // Optimized timeouts for Puppeteer 23.x performance
1173
+ protocolTimeout: 60000, // Reduced from 60s to 45s for faster failure detection
1174
+ slowMo: 0, // No artificial delays
1175
+ defaultViewport: null, // Use system default viewport
1176
+ ignoreDefaultArgs: ['--enable-automation'] // Avoid automation detection
1104
1177
  });
1105
1178
 
1106
1179
  // Store the user data directory on the browser object for cleanup
@@ -1117,6 +1190,15 @@ function setupFrameHandling(page, forceDebug) {
1117
1190
  // launch with no safe browsing
1118
1191
  let browser = await createBrowser();
1119
1192
  if (forceDebug) console.log(formatLogMessage('debug', `Launching browser with headless: ${launchHeadless}`));
1193
+
1194
+ // Enhanced browser validation for Puppeteer 23.x
1195
+ try {
1196
+ const browserVersion = await browser.version();
1197
+ if (forceDebug) console.log(formatLogMessage('debug', `Browser launched successfully: ${browserVersion}`));
1198
+ } catch (versionError) {
1199
+ console.error(formatLogMessage('error', `Browser version check failed: ${versionError.message}`));
1200
+ throw new Error(`Browser startup validation failed: ${versionError.message}`);
1201
+ }
1120
1202
 
1121
1203
  // Log which headless mode is being used
1122
1204
  if (forceDebug && launchHeadless) {
@@ -1246,6 +1328,16 @@ function setupFrameHandling(page, forceDebug) {
1246
1328
  // Track the effective current URL for first-party detection (updates after redirects)
1247
1329
  let effectiveCurrentUrl = currentUrl;
1248
1330
 
1331
+ // Enhanced error types for Puppeteer 23.x compatibility
1332
+ const CRITICAL_BROWSER_ERRORS = [
1333
+ 'Protocol error',
1334
+ 'Target closed',
1335
+ 'Browser has been closed',
1336
+ 'Browser protocol broken',
1337
+ 'Browser process exited',
1338
+ 'Browser disconnected'
1339
+ ];
1340
+
1249
1341
  try {
1250
1342
  // Health check before creating new page
1251
1343
  const isHealthy = await isBrowserHealthy(browserInstance);
@@ -1267,11 +1359,17 @@ function setupFrameHandling(page, forceDebug) {
1267
1359
  throw new Error('Browser process was killed - restart required');
1268
1360
  }
1269
1361
  page = await browserInstance.newPage();
1362
+
1363
+ // Enhanced page validation for Puppeteer 23.x
1364
+ if (!page || page.isClosed()) {
1365
+ throw new Error('Failed to create valid page instance');
1366
+ }
1270
1367
 
1271
1368
  // Set aggressive timeouts for problematic operations
1272
- page.setDefaultTimeout(Math.min(timeout, 15000)); // Reduced for Puppeteer 22.x
1273
- page.setDefaultNavigationTimeout(Math.min(timeout, 18000)); // Reduced for faster fails
1274
- // Aggressive timeouts prevent hanging in Puppeteer 22.x
1369
+ // Optimized timeouts for Puppeteer 23.x responsiveness
1370
+ page.setDefaultTimeout(Math.min(timeout, 20000)); // Reduced from 15000 for faster failures
1371
+ page.setDefaultNavigationTimeout(Math.min(timeout, 25000)); // Reduced from 18000
1372
+ // Aggressive timeouts prevent hanging in Puppeteer 23.x while maintaining speed
1275
1373
 
1276
1374
  page.on('console', (msg) => {
1277
1375
  if (forceDebug && msg.type() === 'error') console.log(`[debug] Console error: ${msg.text()}`);
@@ -1282,6 +1380,57 @@ function setupFrameHandling(page, forceDebug) {
1282
1380
  if (forceDebug) console.log(formatLogMessage('debug', `Page crashed: ${err.message}`));
1283
1381
  // Don't throw here as it might cause hanging - let the timeout handle it
1284
1382
  });
1383
+
1384
+ // Enhanced error handling for Puppeteer 23.x
1385
+ page.on('pageerror', (err) => {
1386
+ // Safe error message extraction for Puppeteer 23.x compatibility
1387
+ const getErrorMessage = (error) => {
1388
+ if (!error) return 'Unknown error';
1389
+ if (typeof error === 'string') return error;
1390
+ if (error.message) return error.message;
1391
+ if (error.toString && typeof error.toString === 'function') {
1392
+ try {
1393
+ return error.toString();
1394
+ } catch (toStringErr) {
1395
+ return 'Error object toString failed';
1396
+ }
1397
+ }
1398
+ return JSON.stringify(error) || 'Unparseable error object';
1399
+ };
1400
+
1401
+ const errorMessage = getErrorMessage(err);
1402
+
1403
+ // Handle specific service worker errors
1404
+ if (errorMessage.includes('ServiceWorker') ||
1405
+ errorMessage.includes('service worker') ||
1406
+ errorMessage.includes('TCPusher service worker') ||
1407
+ errorMessage.includes('failed to register')) {
1408
+ if (forceDebug) {
1409
+ console.log(formatLogMessage('debug', `Service worker error suppressed: ${errorMessage}`));
1410
+ }
1411
+ // Don't propagate service worker errors
1412
+ return;
1413
+ }
1414
+
1415
+ // Handle network-related service worker errors
1416
+ if (errorMessage.includes('TypeError: failed to register') ||
1417
+ errorMessage.includes('SecurityError') ||
1418
+ errorMessage.includes('The operation is insecure')) {
1419
+ if (forceDebug) {
1420
+ console.log(formatLogMessage('debug', `Registration security error suppressed: ${errorMessage}`));
1421
+ }
1422
+ return;
1423
+ }
1424
+
1425
+ // Log other page errors normally
1426
+ if (forceDebug) {
1427
+ console.log(formatLogMessage('debug', `Page JavaScript error: ${errorMessage}`));
1428
+ }
1429
+ });
1430
+
1431
+ page.on('response', (response) => {
1432
+ // Response handler - removed incorrect error logging
1433
+ });
1285
1434
 
1286
1435
  // Apply flowProxy timeouts if detection is enabled
1287
1436
  if (flowproxyDetection) {
@@ -1310,14 +1459,36 @@ function setupFrameHandling(page, forceDebug) {
1310
1459
  // from within the page context at the earliest possible moment.
1311
1460
  const originalFetch = window.fetch;
1312
1461
  window.fetch = (...args) => {
1313
- console.log('[evalOnDoc][fetch]', args[0]); // Log fetch requests
1314
- return originalFetch.apply(this, args);
1462
+ try {
1463
+ console.log('[evalOnDoc][fetch]', args[0]); // Log fetch requests
1464
+ const fetchPromise = originalFetch.apply(this, args);
1465
+
1466
+ // Add network error handling to prevent page errors
1467
+ return fetchPromise.catch(fetchErr => {
1468
+ console.log('[evalOnDoc][fetch-error]', args[0], fetchErr.message);
1469
+ throw fetchErr; // Re-throw to maintain normal error flow
1470
+ });
1471
+ } catch (fetchWrapperErr) {
1472
+ console.log('[evalOnDoc][fetch-wrapper-error]', fetchWrapperErr.message);
1473
+ return originalFetch.apply(this, args);
1474
+ }
1315
1475
  };
1316
1476
 
1317
1477
  const originalXHROpen = XMLHttpRequest.prototype.open;
1318
- XMLHttpRequest.prototype.open = function (method, xhrUrl) { // Renamed 'url' to 'xhrUrl' to avoid conflict
1319
- console.log('[evalOnDoc][xhr]', xhrUrl); // Log XHR requests
1320
- return originalXHROpen.apply(this, arguments);
1478
+ XMLHttpRequest.prototype.open = function (method, xhrUrl) {
1479
+ try {
1480
+ console.log('[evalOnDoc][xhr]', xhrUrl); // Log XHR requests
1481
+
1482
+ // Add error handling for XHR
1483
+ this.addEventListener('error', function(event) {
1484
+ console.log('[evalOnDoc][xhr-error]', xhrUrl, 'Network error occurred');
1485
+ });
1486
+
1487
+ return originalXHROpen.apply(this, arguments);
1488
+ } catch (xhrOpenErr) {
1489
+ console.log('[evalOnDoc][xhr-open-error]', xhrOpenErr.message);
1490
+ return originalXHROpen.apply(this, arguments);
1491
+ }
1321
1492
  };
1322
1493
  });
1323
1494
  } catch (evalErr) {
@@ -2105,10 +2276,22 @@ function setupFrameHandling(page, forceDebug) {
2105
2276
 
2106
2277
  // Use faster defaults for sites with long timeouts to improve responsiveness
2107
2278
  const isFastSite = timeout <= 15000;
2108
- const defaultWaitUntil = 'domcontentloaded'; // Always use faster option in Puppeteer 22.x
2279
+ const defaultWaitUntil = 'domcontentloaded'; // Always use faster option in Puppeteer 23.x
2280
+
2281
+ // Enhanced navigation options for Puppeteer 23.x
2109
2282
  const defaultGotoOptions = {
2110
2283
  waitUntil: defaultWaitUntil,
2111
- timeout: Math.min(timeout, 20000) // Cap timeout for faster failures
2284
+ timeout: Math.min(timeout, 30000), // Reduced from 20000 for faster failures
2285
+ // Puppeteer 23.x: Fixed referrer header handling
2286
+ ...(siteConfig.referrer_headers && (() => {
2287
+ const referrerUrl = Array.isArray(siteConfig.referrer_headers)
2288
+ ? siteConfig.referrer_headers[0]
2289
+ : siteConfig.referrer_headers;
2290
+ // Ensure referrer is a valid string URL, not an object
2291
+ return typeof referrerUrl === 'string' && referrerUrl.startsWith('http')
2292
+ ? { referer: referrerUrl }
2293
+ : {};
2294
+ })())
2112
2295
  };
2113
2296
  const gotoOptions = siteConfig.goto_options
2114
2297
  ? { ...defaultGotoOptions, ...siteConfig.goto_options } : defaultGotoOptions;
@@ -2132,20 +2315,30 @@ function setupFrameHandling(page, forceDebug) {
2132
2315
  console.log(formatLogMessage('debug', `Full redirect chain: ${redirectChain.join(' → ')}`));
2133
2316
  }
2134
2317
 
2135
- // Update currentUrl for all subsequent processing to use the final redirected URL
2136
- currentUrl = finalUrl;
2318
+ // VALIDATION: Only update currentUrl if finalUrl is a valid HTTP/HTTPS URL
2319
+ if (shouldProcessUrl(finalUrl, forceDebug)) {
2320
+ // Update currentUrl for all subsequent processing to use the final redirected URL
2321
+ currentUrl = finalUrl;
2137
2322
 
2138
- // IMPORTANT: Also update effectiveCurrentUrl for first-party detection
2139
- // This ensures the request handler uses the redirected domain for party detection
2140
- effectiveCurrentUrl = finalUrl;
2141
-
2142
- // Update the redirect domains to exclude from matching
2143
- if (redirectDomains && redirectDomains.length > 0) {
2144
- redirectDomainsToExclude = redirectDomains;
2323
+ // IMPORTANT: Also update effectiveCurrentUrl for first-party detection
2324
+ effectiveCurrentUrl = finalUrl;
2145
2325
 
2326
+ // Update the redirect domains to exclude from matching
2327
+ if (redirectDomains && redirectDomains.length > 0) {
2328
+ redirectDomainsToExclude = redirectDomains;
2329
+
2330
+ if (forceDebug) {
2331
+ console.log(formatLogMessage('debug', `Excluding redirect domains from matching: ${redirectDomains.join(', ')}`));
2332
+ }
2333
+ }
2334
+ } else {
2335
+ // Invalid final URL - don't update currentUrl, treat as failed redirect
2336
+ console.warn(`⚠ Redirect to invalid URL ignored: ${originalDomain} → ${finalUrl}`);
2146
2337
  if (forceDebug) {
2147
- console.log(formatLogMessage('debug', `Excluding redirect domains from matching: ${redirectDomains.join(', ')}`));
2338
+ console.log(formatLogMessage('debug', `Redirect chain ended with invalid URL, keeping original: ${originalUrl}`));
2148
2339
  }
2340
+ // Keep processing with the original URL or throw an error
2341
+ throw new Error(`Redirect resulted in invalid URL: ${finalUrl}`);
2149
2342
  }
2150
2343
  }
2151
2344
  }
@@ -2235,17 +2428,17 @@ function setupFrameHandling(page, forceDebug) {
2235
2428
 
2236
2429
  const delayMs = siteConfig.delay || 4000;
2237
2430
 
2238
- // Aggressive optimization for Puppeteer 22.x responsiveness
2431
+ // Optimized delays for Puppeteer 23.x performance
2239
2432
  const isFastSite = timeout <= 15000;
2240
- const networkIdleTime = 2000; // Reduced for all sites
2241
- const networkIdleTimeout = Math.min(timeout / 2, 8000); // Much shorter timeout
2242
- const actualDelay = Math.min(delayMs, 1500); // Reduced delay for all sites
2433
+ const networkIdleTime = 2000; // Balanced: 2s for reliable network detection
2434
+ const networkIdleTimeout = Math.min(timeout / 2, 10000); // Balanced: 10s timeout
2435
+ const actualDelay = Math.min(delayMs, 2000); // Balanced: 2s delay for stability
2243
2436
 
2244
2437
  await page.waitForNetworkIdle({
2245
2438
  idleTime: networkIdleTime,
2246
2439
  timeout: networkIdleTimeout
2247
2440
  });
2248
- // Use fast timeout helper for Puppeteer 22.x compatibility with better performance
2441
+ // Use fast timeout helper for Puppeteer 23.x compatibility with better performance
2249
2442
  await fastTimeout(actualDelay);
2250
2443
 
2251
2444
  // Apply additional delay for flowProxy if detected
@@ -2255,8 +2448,7 @@ function setupFrameHandling(page, forceDebug) {
2255
2448
  await fastTimeout(additionalDelay);
2256
2449
  }
2257
2450
 
2258
- // Replace deprecated page.waitForTimeout patterns with fast timeout helper
2259
- // This ensures compatibility with Puppeteer 22.x where waitForTimeout was removed
2451
+ // Use fast timeout helper for consistent Puppeteer 23.x compatibility
2260
2452
 
2261
2453
  for (let i = 1; i < (siteConfig.reload || 1); i++) {
2262
2454
  if (siteConfig.clear_sitedata === true) {
@@ -2349,35 +2541,23 @@ function setupFrameHandling(page, forceDebug) {
2349
2541
  }
2350
2542
 
2351
2543
  } catch (err) {
2352
- // Enhanced error handling with rule preservation for partial matches
2353
- if (err.message.includes('Runtime.callFunctionOn timed out') ||
2354
- err.message.includes('Protocol error') ||
2355
- err.message.includes('Target closed') ||
2356
- err.message.includes('Browser has been closed')) {
2357
- console.error(formatLogMessage('error', `Critical browser protocol error on ${currentUrl}: ${err.message}`));
2544
+ // Only restart for truly fatal browser errors
2545
+ const isFatalError = CRITICAL_BROWSER_ERRORS.some(errorType =>
2546
+ err.message.includes(errorType)
2547
+ ) && !err.message.includes('timeout') && !err.message.includes('Navigation');
2548
+
2549
+ if (isFatalError) {
2550
+ console.error(formatLogMessage('error', `Fatal browser error on ${currentUrl}: ${err.message}`));
2358
2551
  return {
2359
2552
  url: currentUrl,
2360
2553
  rules: [],
2361
2554
  success: false,
2362
2555
  needsImmediateRestart: true,
2363
- error: `Critical protocol error: ${err.message}`
2556
+ error: `Fatal error: ${err.message}`,
2557
+ errorType: 'fatal'
2364
2558
  };
2365
2559
  }
2366
-
2367
- if (err.message.includes('Protocol error') ||
2368
- err.message.includes('Target closed') ||
2369
- err.message.includes('Browser process was killed') ||
2370
- err.message.includes('Browser protocol broken')) {
2371
- console.error(formatLogMessage('error', `Critical browser error on ${currentUrl}: ${err.message}`));
2372
- return {
2373
- url: currentUrl,
2374
- rules: [],
2375
- success: false,
2376
- needsImmediateRestart: true,
2377
- error: err.message
2378
- };
2379
- }
2380
-
2560
+
2381
2561
  // For other errors, preserve any matches we found before the error
2382
2562
  if (matchedDomains && (matchedDomains.size > 0 || (matchedDomains instanceof Map && matchedDomains.size > 0))) {
2383
2563
  const globalOptions = {
@@ -2549,6 +2729,23 @@ function setupFrameHandling(page, forceDebug) {
2549
2729
  // Check if any results indicate immediate restart is needed
2550
2730
  const needsImmediateRestart = siteResults.some(r => r.needsImmediateRestart);
2551
2731
 
2732
+ // Enhanced error reporting for Puppeteer 23.x
2733
+ if (forceDebug) {
2734
+ const errorSummary = siteResults.reduce((acc, result) => {
2735
+ if (!result.success && result.errorType) {
2736
+ acc[result.errorType] = (acc[result.errorType] || 0) + 1;
2737
+ }
2738
+ return acc;
2739
+ }, {});
2740
+
2741
+ if (Object.keys(errorSummary).length > 0) {
2742
+ console.log(formatLogMessage('debug', `Site ${siteIndex + 1} error summary:`));
2743
+ Object.entries(errorSummary).forEach(([errorType, count]) => {
2744
+ console.log(formatLogMessage('debug', ` ${errorType}: ${count} error(s)`));
2745
+ });
2746
+ }
2747
+ }
2748
+
2552
2749
  results.push(...siteResults);
2553
2750
 
2554
2751
  processedUrlCount += siteUrlCount;
@@ -2562,6 +2759,19 @@ function setupFrameHandling(page, forceDebug) {
2562
2759
 
2563
2760
  // Force browser restart immediately
2564
2761
  try {
2762
+ // Enhanced emergency restart for Puppeteer 23.x
2763
+ if (forceDebug) {
2764
+ console.log(formatLogMessage('debug', `Emergency restart triggered by errors: ${siteResults.filter(r => r.needsImmediateRestart).map(r => r.error).join(', ')}`));
2765
+ }
2766
+
2767
+ // Try to gracefully close all pages first
2768
+ try {
2769
+ const pages = await browser.pages();
2770
+ await Promise.all(pages.map(page => page.close().catch(() => {})));
2771
+ } catch (pageCloseErr) {
2772
+ if (forceDebug) console.log(formatLogMessage('debug', `Page cleanup during emergency restart failed: ${pageCloseErr.message}`));
2773
+ }
2774
+
2565
2775
  await handleBrowserExit(browser, { forceDebug, timeout: 5000, exitOnFailure: false, cleanTempFiles: true, comprehensiveCleanup: removeTempFiles });
2566
2776
  // Additional cleanup after emergency restart
2567
2777
  if (removeTempFiles) {
@@ -2702,7 +2912,14 @@ function setupFrameHandling(page, forceDebug) {
2702
2912
 
2703
2913
  // Perform comprehensive final cleanup using enhanced browserexit module
2704
2914
  if (forceDebug) console.log(formatLogMessage('debug', `Starting comprehensive browser cleanup...`));
2705
-
2915
+
2916
+ // Enhanced final validation for Puppeteer 23.x
2917
+ try {
2918
+ const isStillConnected = browser.isConnected();
2919
+ if (forceDebug) console.log(formatLogMessage('debug', `Browser connection status before cleanup: ${isStillConnected}`));
2920
+ } catch (connErr) {
2921
+ if (forceDebug) console.log(formatLogMessage('debug', `Browser connection check failed: ${connErr.message}`));
2922
+ }
2706
2923
 
2707
2924
  const cleanupResult = await handleBrowserExit(browser, {
2708
2925
  forceDebug,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "1.0.62",
3
+ "version": "1.0.64",
4
4
  "description": "A Puppeteer-based network scanner for analyzing web traffic, generating adblock filter rules, and identifying third-party requests. Features include fingerprint spoofing, Cloudflare bypass, content analysis with curl/grep, and multiple output formats.",
5
5
  "main": "nwss.js",
6
6
  "scripts": {