@fanboynz/network-scanner 1.0.88 → 1.0.89

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.
Files changed (2) hide show
  1. package/nwss.js +114 -67
  2. package/package.json +1 -1
package/nwss.js CHANGED
@@ -1,4 +1,4 @@
1
- // === Network scanner script (nwss.js) v1.0.88 ===
1
+ // === Network scanner script (nwss.js) v1.0.89 ===
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
@@ -125,7 +125,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
125
125
  const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive } = require('./lib/browserhealth');
126
126
 
127
127
  // --- Script Configuration & Constants ---
128
- const VERSION = '1.0.88'; // Script version
128
+ const VERSION = '1.0.89'; // Script version
129
129
 
130
130
  // get startTime
131
131
  const startTime = Date.now();
@@ -1460,21 +1460,7 @@ function setupFrameHandling(page, forceDebug) {
1460
1460
  ];
1461
1461
 
1462
1462
  try {
1463
- // Health check before creating new page
1464
- const isHealthy = await isBrowserHealthy(browserInstance);
1465
- if (!isHealthy) {
1466
- if (forceDebug) {
1467
- console.log(formatLogMessage('debug', `Browser health degraded before processing ${currentUrl} - forcing immediate restart`));
1468
- }
1469
- // Return special code to trigger immediate browser restart
1470
- return {
1471
- url: currentUrl,
1472
- rules: [],
1473
- success: false,
1474
- needsImmediateRestart: true,
1475
- error: 'Browser health degraded - restart required'
1476
- };
1477
- }
1463
+
1478
1464
  // Check for Protocol timeout errors that indicate browser is broken
1479
1465
  if (browserInstance.process() && browserInstance.process().killed) {
1480
1466
  throw new Error('Browser process was killed - restart required');
@@ -1486,22 +1472,7 @@ function setupFrameHandling(page, forceDebug) {
1486
1472
  throw new Error('Failed to create valid page instance');
1487
1473
  }
1488
1474
 
1489
- // Additional health check after page creation but before critical setup
1490
- const stillHealthy = await isQuicklyResponsive(browserInstance, 3000);
1491
-
1492
- if (!stillHealthy) {
1493
- if (forceDebug) {
1494
- console.log(formatLogMessage('debug', `Browser unresponsive during page setup for ${currentUrl} - triggering restart`));
1495
- }
1496
- return {
1497
- url: currentUrl,
1498
- rules: [],
1499
- success: false,
1500
- needsImmediateRestart: true,
1501
- error: 'Browser became unresponsive during page setup - restart required'
1502
- };
1503
- }
1504
-
1475
+
1505
1476
  // Set aggressive timeouts for problematic operations
1506
1477
  // Optimized timeouts for Puppeteer 23.x responsiveness
1507
1478
  page.setDefaultTimeout(Math.min(timeout, TIMEOUTS.DEFAULT_PAGE_REDUCED));
@@ -1582,6 +1553,8 @@ function setupFrameHandling(page, forceDebug) {
1582
1553
  // --- START: evaluateOnNewDocument for Fetch/XHR Interception (Moved and Fixed) ---
1583
1554
  // This script is injected if --eval-on-doc is used or siteConfig.evaluateOnNewDocument is true.
1584
1555
  const shouldInjectEvalForPage = siteConfig.evaluateOnNewDocument === true || globalEvalOnDoc;
1556
+ let evalOnDocSuccess = false; // Track injection success for fallback logic
1557
+
1585
1558
  if (shouldInjectEvalForPage) {
1586
1559
  if (forceDebug) {
1587
1560
  if (globalEvalOnDoc) {
@@ -1590,24 +1563,29 @@ function setupFrameHandling(page, forceDebug) {
1590
1563
  console.log(formatLogMessage('debug', `[evalOnDoc] Site-specific Fetch/XHR interception enabled for: ${currentUrl}`));
1591
1564
  }
1592
1565
  }
1593
- // Quick browser health check before script injection
1566
+
1567
+ // Strategy 1: Try full injection with health check
1594
1568
  let browserResponsive = false;
1595
1569
  try {
1596
1570
  await Promise.race([
1597
1571
  browserInstance.version(), // Quick responsiveness test
1598
1572
  new Promise((_, reject) =>
1599
- setTimeout(() => reject(new Error('Browser health check timeout')), 5000)
1573
+ setTimeout(() => reject(new Error('Browser health check timeout')), 3000)
1600
1574
  )
1601
1575
  ]);
1602
1576
  browserResponsive = true;
1603
1577
  } catch (healthErr) {
1604
- console.warn(formatLogMessage('warn', `[evalOnDoc] Browser unresponsive for ${currentUrl}: ${healthErr.message} - skipping script injection`));
1578
+ if (forceDebug) {
1579
+ console.log(formatLogMessage('debug', `[evalOnDoc] Browser health check failed: ${healthErr.message}`));
1580
+ }
1605
1581
  browserResponsive = false;
1606
1582
  }
1607
1583
 
1584
+ // Strategy 2: Try injection with reduced complexity if browser is responsive
1608
1585
  if (browserResponsive) {
1609
- try {
1610
- await page.evaluateOnNewDocument(() => {
1586
+ try {
1587
+ await Promise.race([
1588
+ page.evaluateOnNewDocument(() => {
1611
1589
  // Prevent infinite reload loops
1612
1590
  let reloadCount = 0;
1613
1591
  const MAX_RELOADS = 2;
@@ -1668,18 +1646,62 @@ function setupFrameHandling(page, forceDebug) {
1668
1646
  return originalXHROpen.apply(this, arguments);
1669
1647
  }
1670
1648
  };
1671
- });
1672
- } catch (evalErr) {
1673
- if (evalErr.message.includes('timed out') || evalErr.message.includes('ProtocolError')) {
1674
- console.warn(formatLogMessage('warn', `[evalOnDoc] Script injection protocol timeout for ${currentUrl} - continuing without XHR/Fetch interception`));
1675
- } else {
1676
- console.warn(formatLogMessage('warn', `[evalOnDoc] Failed to set up Fetch/XHR interception for ${currentUrl}: ${evalErr.message}`));
1649
+ }),
1650
+ new Promise((_, reject) =>
1651
+ setTimeout(() => reject(new Error('Injection timeout')), 8000)
1652
+ )
1653
+ ]);
1654
+ evalOnDocSuccess = true;
1655
+ if (forceDebug) {
1656
+ console.log(formatLogMessage('debug', `[evalOnDoc] Full injection successful for ${currentUrl}`));
1677
1657
  }
1678
- }
1658
+ } catch (fullInjectionErr) {
1659
+ if (forceDebug) {
1660
+ console.log(formatLogMessage('debug', `[evalOnDoc] Full injection failed: ${fullInjectionErr.message}, trying simplified fallback`));
1661
+ }
1662
+
1663
+ // Strategy 3: Fallback - Try minimal injection (just fetch monitoring)
1664
+ try {
1665
+ await Promise.race([
1666
+ page.evaluateOnNewDocument(() => {
1667
+ // Minimal injection - just fetch monitoring
1668
+ if (window.fetch) {
1669
+ const originalFetch = window.fetch;
1670
+ window.fetch = (...args) => {
1671
+ try {
1672
+ console.log('[evalOnDoc][fetch-minimal]', args[0]);
1673
+ return originalFetch.apply(this, args);
1674
+ } catch (err) {
1675
+ return originalFetch.apply(this, args);
1676
+ }
1677
+ };
1678
+ }
1679
+ }),
1680
+ new Promise((_, reject) =>
1681
+ setTimeout(() => reject(new Error('Minimal injection timeout')), 3000)
1682
+ )
1683
+ ]);
1684
+ evalOnDocSuccess = true;
1685
+ if (forceDebug) {
1686
+ console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection successful for ${currentUrl}`));
1687
+ }
1688
+ } catch (minimalInjectionErr) {
1689
+ if (forceDebug) {
1690
+ console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection also failed: ${minimalInjectionErr.message}`));
1691
+ }
1692
+ evalOnDocSuccess = false;
1693
+ }
1694
+ }
1679
1695
  } else {
1680
1696
  if (forceDebug) {
1681
- console.log(formatLogMessage('debug', `[evalOnDoc] Continuing ${currentUrl} without XHR/Fetch interception due to browser health`));
1697
+ console.log(formatLogMessage('debug', `[evalOnDoc] Browser unresponsive, skipping injection for ${currentUrl}`));
1682
1698
  }
1699
+ evalOnDocSuccess = false;
1700
+ }
1701
+
1702
+ // Final status logging
1703
+ if (!evalOnDocSuccess) {
1704
+ console.warn(formatLogMessage('warn', `[evalOnDoc] All injection strategies failed for ${currentUrl} - continuing with standard request monitoring only`));
1683
1705
  }
1684
1706
  }
1685
1707
  // --- END: evaluateOnNewDocument for Fetch/XHR Interception ---
@@ -3132,20 +3154,31 @@ function setupFrameHandling(page, forceDebug) {
3132
3154
  const batchEnd = Math.min(batchStart + RESOURCE_CLEANUP_INTERVAL, totalUrls);
3133
3155
  const currentBatch = allTasks.slice(batchStart, batchEnd);
3134
3156
 
3135
- // Check browser health before processing each site
3136
- const healthCheck = await monitorBrowserHealth(browser, {}, {
3137
- siteIndex: Math.floor(batchStart / RESOURCE_CLEANUP_INTERVAL),
3138
- totalSites: Math.ceil(totalUrls / RESOURCE_CLEANUP_INTERVAL),
3139
- urlsSinceCleanup: urlsSinceLastCleanup,
3140
- cleanupInterval: RESOURCE_CLEANUP_INTERVAL,
3141
- forceDebug,
3142
- silentMode
3143
- });
3144
-
3145
- // Check if browser was unhealthy during recent processing
3146
- const recentResults = results.slice(-3);
3147
- const hasRecentFailures = recentResults.filter(r => !r.success).length >= 2;
3148
- const shouldRestartFromFailures = hasRecentFailures && urlsSinceLastCleanup > 3;
3157
+ // IMPROVED: Only check health if we have indicators of problems
3158
+ let healthCheck = { shouldRestart: false, reason: null };
3159
+ const recentResults = results.slice(-8); // Check more results for better pattern detection
3160
+ const recentFailureRate = recentResults.length > 0 ?
3161
+ recentResults.filter(r => !r.success).length / recentResults.length : 0;
3162
+ const hasHighFailureRate = recentFailureRate > 0.75; // 75% failure threshold (more conservative)
3163
+ const hasCriticalErrors = recentResults.filter(r => r.needsImmediateRestart).length > 2;
3164
+
3165
+ // Only run health checks when we have STRONG indicators of problems
3166
+ if (urlsSinceLastCleanup > 15 && (
3167
+ (hasHighFailureRate && recentResults.length >= 5) || // Need sufficient sample size
3168
+ hasCriticalErrors ||
3169
+ urlsSinceLastCleanup > RESOURCE_CLEANUP_INTERVAL * 0.9 // Very close to cleanup limit
3170
+ )) {
3171
+ healthCheck = await monitorBrowserHealth(browser, {}, {
3172
+ siteIndex: Math.floor(batchStart / RESOURCE_CLEANUP_INTERVAL),
3173
+ totalSites: Math.ceil(totalUrls / RESOURCE_CLEANUP_INTERVAL),
3174
+ urlsSinceCleanup: urlsSinceLastCleanup,
3175
+ cleanupInterval: RESOURCE_CLEANUP_INTERVAL,
3176
+ forceDebug,
3177
+ silentMode
3178
+ });
3179
+ } else if (forceDebug && urlsSinceLastCleanup > 10) {
3180
+ console.log(formatLogMessage('debug', `Skipping health check: failure rate ${Math.round(recentFailureRate * 100)}%, critical errors: ${hasCriticalErrors ? 'yes' : 'no'}`));
3181
+ }
3149
3182
 
3150
3183
  const batchSize = currentBatch.length;
3151
3184
 
@@ -3155,17 +3188,21 @@ function setupFrameHandling(page, forceDebug) {
3155
3188
  // Check if processing this entire site would exceed cleanup interval OR health check suggests restart
3156
3189
  const wouldExceedLimit = urlsSinceLastCleanup + batchSize >= Math.min(RESOURCE_CLEANUP_INTERVAL, 100);
3157
3190
  const isNotLastBatch = batchEnd < totalUrls;
3191
+ // IMPROVED: More restrictive health-based restart conditions
3192
+ const shouldRestartFromHealth = healthCheck.shouldRestart &&
3193
+ !healthCheck.reason?.includes('Scheduled cleanup') &&
3194
+ (healthCheck.reason?.includes('Critical') || healthCheck.reason?.includes('disconnected'));
3158
3195
 
3159
3196
  // Restart browser if we've processed enough URLs, health check suggests it, and this isn't the last site
3160
- if ((wouldExceedLimit || healthCheck.shouldRestart || shouldRestartFromFailures) && urlsSinceLastCleanup > 0 && isNotLastBatch) {
3197
+ if ((wouldExceedLimit || shouldRestartFromHealth || (hasHighFailureRate && recentResults.length >= 6)) && urlsSinceLastCleanup > 8 && isNotLastBatch) {
3161
3198
 
3162
3199
  let restartReason = 'Unknown';
3163
- if (healthCheck.shouldRestart) {
3200
+ if (shouldRestartFromHealth) {
3164
3201
  restartReason = healthCheck.reason;
3165
- } else if (shouldRestartFromFailures) {
3166
- restartReason = 'Multiple recent failures detected';
3202
+ } else if (hasHighFailureRate) {
3203
+ restartReason = `High failure rate: ${Math.round(recentFailureRate * 100)}% in recent batch`;
3167
3204
  } else if (wouldExceedLimit) {
3168
- restartReason = `Processed ${urlsSinceLastCleanup} URLs`;
3205
+ restartReason = `Processed ${urlsSinceLastCleanup} URLs (scheduled maintenance)`;
3169
3206
  }
3170
3207
 
3171
3208
  if (!silentMode) {
@@ -3233,8 +3270,18 @@ function setupFrameHandling(page, forceDebug) {
3233
3270
  const batchTasks = currentBatch.map(task => originalLimit(() => processUrl(task.url, task.config, browser)));
3234
3271
  const batchResults = await Promise.all(batchTasks);
3235
3272
 
3236
- // Check if any results indicate immediate restart is needed
3237
- const needsImmediateRestart = batchResults.some(r => r.needsImmediateRestart);
3273
+ // IMPROVED: Much more conservative emergency restart logic
3274
+ const criticalRestartCount = batchResults.filter(r => r.needsImmediateRestart).length;
3275
+ // Require either:
3276
+ // - More than 50% of batch has critical errors, OR
3277
+ // - At least 3 critical errors in any size batch
3278
+ const restartThreshold = Math.max(3, Math.floor(batchSize * 0.5)); // 50% of batch or min 3
3279
+ const needsImmediateRestart = criticalRestartCount >= restartThreshold && criticalRestartCount >= 2;
3280
+
3281
+ // Log restart decision for debugging
3282
+ if (forceDebug && criticalRestartCount > 0) {
3283
+ console.log(formatLogMessage('debug', `Emergency restart decision: ${criticalRestartCount}/${batchSize} critical errors (threshold: ${restartThreshold}, restart: ${needsImmediateRestart ? 'YES' : 'NO'})`));
3284
+ }
3238
3285
 
3239
3286
  // Log completion of concurrent processing
3240
3287
  if (forceDebug) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "1.0.88",
3
+ "version": "1.0.89",
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": {