@fanboynz/network-scanner 1.0.57 → 1.0.58
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/lib/smart-cache.js +318 -12
- package/nwss.js +58 -16
- package/package.json +1 -1
package/lib/smart-cache.js
CHANGED
|
@@ -14,6 +14,11 @@ const { formatLogMessage } = require('./colorize');
|
|
|
14
14
|
*/
|
|
15
15
|
class SmartCache {
|
|
16
16
|
constructor(options = {}) {
|
|
17
|
+
// Calculate dynamic values first
|
|
18
|
+
const concurrency = options.concurrency || 6;
|
|
19
|
+
const optimalHeapLimit = this._calculateOptimalHeapLimit(concurrency);
|
|
20
|
+
const checkInterval = this._calculateCheckInterval(concurrency);
|
|
21
|
+
|
|
17
22
|
this.options = {
|
|
18
23
|
maxSize: options.maxSize || 5000,
|
|
19
24
|
ttl: options.ttl || 1000 * 60 * 60, // 1 hour default
|
|
@@ -24,8 +29,18 @@ class SmartCache {
|
|
|
24
29
|
persistencePath: options.persistencePath || '.cache',
|
|
25
30
|
forceDebug: options.forceDebug || false,
|
|
26
31
|
autoSave: options.autoSave !== false,
|
|
27
|
-
autoSaveInterval: options.autoSaveInterval || 60000 // 1 minute
|
|
32
|
+
autoSaveInterval: options.autoSaveInterval || 60000, // 1 minute
|
|
33
|
+
maxHeapUsage: options.maxHeapUsage || optimalHeapLimit,
|
|
34
|
+
memoryCheckInterval: options.memoryCheckInterval || checkInterval,
|
|
35
|
+
concurrency: concurrency,
|
|
36
|
+
aggressiveMode: options.aggressiveMode || false
|
|
28
37
|
};
|
|
38
|
+
|
|
39
|
+
// Add save debouncing
|
|
40
|
+
this.lastSaveTime = 0;
|
|
41
|
+
this.saveInProgress = false;
|
|
42
|
+
this.saveTimeout = null;
|
|
43
|
+
this.pendingSave = false;
|
|
29
44
|
|
|
30
45
|
// Initialize cache layers
|
|
31
46
|
this._initializeCaches();
|
|
@@ -42,6 +57,34 @@ class SmartCache {
|
|
|
42
57
|
if (this.options.enablePersistence && this.options.autoSave) {
|
|
43
58
|
this._setupAutoSave();
|
|
44
59
|
}
|
|
60
|
+
|
|
61
|
+
// Set up memory monitoring
|
|
62
|
+
this.memoryCheckInterval = setInterval(() => {
|
|
63
|
+
this._checkMemoryPressure();
|
|
64
|
+
}, this.options.memoryCheckInterval);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Calculate optimal heap limit based on concurrency
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
_calculateOptimalHeapLimit(concurrency) {
|
|
72
|
+
// Base cache needs: 100MB
|
|
73
|
+
// Per concurrent connection: ~75MB average
|
|
74
|
+
// Safety margin: 50%
|
|
75
|
+
const baseCacheMemory = 100 * 1024 * 1024; // 100MB
|
|
76
|
+
const perConnectionMemory = 75 * 1024 * 1024; // 75MB
|
|
77
|
+
const totalEstimated = baseCacheMemory + (concurrency * perConnectionMemory);
|
|
78
|
+
return Math.round(totalEstimated * 0.4); // Cache should use max 40% of estimated total
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Calculate check interval based on concurrency
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
_calculateCheckInterval(concurrency) {
|
|
86
|
+
// Higher concurrency = more frequent checks
|
|
87
|
+
return Math.max(5000, 30000 - (concurrency * 1000)); // 5s min, scales down with concurrency
|
|
45
88
|
}
|
|
46
89
|
|
|
47
90
|
/**
|
|
@@ -57,29 +100,43 @@ class SmartCache {
|
|
|
57
100
|
updateAgeOnHas: false
|
|
58
101
|
});
|
|
59
102
|
|
|
60
|
-
// Pattern matching results cache
|
|
103
|
+
// Pattern matching results cache - reduce size for high concurrency
|
|
104
|
+
const patternCacheSize = this.options.concurrency > 10 ? 500 : 1000;
|
|
61
105
|
this.patternCache = new LRUCache({
|
|
62
|
-
max:
|
|
106
|
+
max: patternCacheSize,
|
|
63
107
|
ttl: this.options.ttl * 2 // Patterns are more stable
|
|
64
108
|
});
|
|
65
109
|
|
|
66
|
-
// Response content cache for
|
|
110
|
+
// Response content cache - aggressive limits for high concurrency
|
|
111
|
+
const responseCacheSize = this.options.concurrency > 10 ? 50 : 200;
|
|
112
|
+
const responseCacheMemory = this.options.concurrency > 10 ? 20 * 1024 * 1024 : 50 * 1024 * 1024;
|
|
67
113
|
this.responseCache = new LRUCache({
|
|
68
|
-
max:
|
|
114
|
+
max: responseCacheSize,
|
|
69
115
|
ttl: 1000 * 60 * 30, // 30 minutes for response content
|
|
70
|
-
maxSize:
|
|
116
|
+
maxSize: responseCacheMemory,
|
|
71
117
|
sizeCalculation: (value) => value.length
|
|
72
118
|
});
|
|
73
119
|
|
|
120
|
+
// Disable response cache entirely for very high concurrency
|
|
121
|
+
if (this.options.concurrency > 15 || this.options.aggressiveMode) {
|
|
122
|
+
this.options.enableResponseCache = false;
|
|
123
|
+
if (this.options.forceDebug) {
|
|
124
|
+
console.log(formatLogMessage('debug',
|
|
125
|
+
`[SmartCache] Response cache disabled for high concurrency (${this.options.concurrency})`
|
|
126
|
+
));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
74
130
|
// WHOIS/DNS results cache
|
|
75
131
|
this.netToolsCache = new LRUCache({
|
|
76
132
|
max: 500,
|
|
77
133
|
ttl: 1000 * 60 * 60 * 24 // 24 hours for WHOIS/DNS
|
|
78
134
|
});
|
|
79
135
|
|
|
80
|
-
// Similarity cache for
|
|
136
|
+
// Similarity cache - reduce for high concurrency
|
|
137
|
+
const similarityCacheSize = this.options.concurrency > 10 ? 1000 : 2000;
|
|
81
138
|
this.similarityCache = new LRUCache({
|
|
82
|
-
max:
|
|
139
|
+
max: similarityCacheSize,
|
|
83
140
|
ttl: this.options.ttl
|
|
84
141
|
});
|
|
85
142
|
|
|
@@ -107,6 +164,9 @@ class SmartCache {
|
|
|
107
164
|
regexCacheHits: 0,
|
|
108
165
|
persistenceLoads: 0,
|
|
109
166
|
persistenceSaves: 0,
|
|
167
|
+
memoryPressureEvents: 0,
|
|
168
|
+
memoryWarnings: 0,
|
|
169
|
+
responseCacheSkips: 0,
|
|
110
170
|
startTime: Date.now()
|
|
111
171
|
};
|
|
112
172
|
}
|
|
@@ -274,6 +334,21 @@ class SmartCache {
|
|
|
274
334
|
cacheResponse(url, content) {
|
|
275
335
|
if (!this.options.enableResponseCache) return;
|
|
276
336
|
|
|
337
|
+
// Skip response caching entirely for very high concurrency
|
|
338
|
+
if (this.options.concurrency > 12) {
|
|
339
|
+
this.stats.responseCacheSkips++;
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Check memory before caching large content
|
|
344
|
+
const memUsage = process.memoryUsage();
|
|
345
|
+
const threshold = this.options.concurrency > 10 ? 0.7 : 0.8; // Lower threshold for high concurrency
|
|
346
|
+
if (memUsage.heapUsed > this.options.maxHeapUsage * threshold) {
|
|
347
|
+
this.stats.responseCacheSkips++;
|
|
348
|
+
this._logMemorySkip('response cache');
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
277
352
|
// Only cache if content is reasonable size
|
|
278
353
|
if (content && content.length < 5 * 1024 * 1024) { // 5MB limit per response
|
|
279
354
|
this.responseCache.set(url, content);
|
|
@@ -350,7 +425,85 @@ class SmartCache {
|
|
|
350
425
|
this.stats.similarityMisses++;
|
|
351
426
|
return null;
|
|
352
427
|
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Monitor memory usage and proactively manage caches
|
|
431
|
+
* @private
|
|
432
|
+
*/
|
|
433
|
+
_checkMemoryPressure() {
|
|
434
|
+
const memUsage = process.memoryUsage();
|
|
435
|
+
const heapUsedMB = Math.round(memUsage.heapUsed / 1024 / 1024);
|
|
436
|
+
const maxHeapMB = Math.round(this.options.maxHeapUsage / 1024 / 1024);
|
|
437
|
+
const usagePercent = (memUsage.heapUsed / this.options.maxHeapUsage) * 100;
|
|
438
|
+
|
|
439
|
+
// Adjust thresholds based on concurrency
|
|
440
|
+
const criticalThreshold = this.options.concurrency > 10 ? 0.85 : 1.0;
|
|
441
|
+
const warningThreshold = this.options.concurrency > 10 ? 0.70 : 0.85;
|
|
442
|
+
const infoThreshold = this.options.concurrency > 10 ? 0.60 : 0.75;
|
|
443
|
+
|
|
444
|
+
// Critical threshold - aggressive cleanup
|
|
445
|
+
if (memUsage.heapUsed > this.options.maxHeapUsage * criticalThreshold) {
|
|
446
|
+
this._performMemoryCleanup('critical', heapUsedMB, maxHeapMB);
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Warning threshold - moderate cleanup
|
|
451
|
+
if (memUsage.heapUsed > this.options.maxHeapUsage * warningThreshold) {
|
|
452
|
+
this._performMemoryCleanup('warning', heapUsedMB, maxHeapMB);
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Info threshold - log only
|
|
457
|
+
if (memUsage.heapUsed > this.options.maxHeapUsage * infoThreshold) {
|
|
458
|
+
this.stats.memoryWarnings++;
|
|
459
|
+
if (this.options.forceDebug) {
|
|
460
|
+
console.log(formatLogMessage('debug',
|
|
461
|
+
`[SmartCache] Memory info: ${heapUsedMB}MB/${maxHeapMB}MB (${usagePercent.toFixed(1)}%)`
|
|
462
|
+
));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
353
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Perform memory cleanup based on severity
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
_performMemoryCleanup(level, heapUsedMB, maxHeapMB) {
|
|
474
|
+
this.stats.memoryPressureEvents++;
|
|
475
|
+
|
|
476
|
+
if (this.options.forceDebug) {
|
|
477
|
+
console.log(formatLogMessage('debug',
|
|
478
|
+
`[SmartCache] Memory ${level}: ${heapUsedMB}MB/${maxHeapMB}MB, performing cleanup...`
|
|
479
|
+
));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (level === 'critical' || this.options.concurrency > 12) {
|
|
483
|
+
// Aggressive cleanup - clear volatile caches
|
|
484
|
+
this.responseCache.clear();
|
|
485
|
+
this.patternCache.clear();
|
|
486
|
+
this.similarityCache.clear();
|
|
487
|
+
|
|
488
|
+
// For very high concurrency, also trim domain cache
|
|
489
|
+
if (this.options.concurrency > 15) {
|
|
490
|
+
const currentSize = this.domainCache.size;
|
|
491
|
+
this.domainCache.clear();
|
|
492
|
+
if (this.options.forceDebug) {
|
|
493
|
+
console.log(formatLogMessage('debug', `[SmartCache] Cleared ${currentSize} domain cache entries`));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
} else if (level === 'warning') {
|
|
497
|
+
// Moderate cleanup - clear largest cache
|
|
498
|
+
this.responseCache.clear();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Force garbage collection if available
|
|
502
|
+
if (global.gc) {
|
|
503
|
+
global.gc();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
354
507
|
/**
|
|
355
508
|
* Get cache statistics
|
|
356
509
|
* @returns {Object} Statistics object
|
|
@@ -364,6 +517,9 @@ class SmartCache {
|
|
|
364
517
|
(this.stats.responseHits + this.stats.responseMisses) || 0;
|
|
365
518
|
const netToolsHitRate = this.stats.netToolsHits /
|
|
366
519
|
(this.stats.netToolsHits + this.stats.netToolsMisses) || 0;
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
const memUsage = process.memoryUsage();
|
|
367
523
|
|
|
368
524
|
return {
|
|
369
525
|
...this.stats,
|
|
@@ -380,7 +536,11 @@ class SmartCache {
|
|
|
380
536
|
regexCacheSize: this.regexCache.size,
|
|
381
537
|
totalCacheEntries: this.domainCache.size + this.patternCache.size +
|
|
382
538
|
this.responseCache.size + this.netToolsCache.size +
|
|
383
|
-
this.similarityCache.size + this.regexCache.size
|
|
539
|
+
this.similarityCache.size + this.regexCache.size,
|
|
540
|
+
memoryUsageMB: Math.round(memUsage.heapUsed / 1024 / 1024),
|
|
541
|
+
memoryMaxMB: Math.round(this.options.maxHeapUsage / 1024 / 1024),
|
|
542
|
+
memoryUsagePercent: ((memUsage.heapUsed / this.options.maxHeapUsage) * 100).toFixed(1) + '%',
|
|
543
|
+
responseCacheMemoryMB: Math.round((this.responseCache.calculatedSize || 0) / 1024 / 1024)
|
|
384
544
|
};
|
|
385
545
|
}
|
|
386
546
|
|
|
@@ -401,6 +561,18 @@ class SmartCache {
|
|
|
401
561
|
}
|
|
402
562
|
}
|
|
403
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Helper method to log memory-related cache skips
|
|
566
|
+
* @private
|
|
567
|
+
*/
|
|
568
|
+
_logMemorySkip(operation) {
|
|
569
|
+
if (this.options.forceDebug) {
|
|
570
|
+
console.log(formatLogMessage('debug',
|
|
571
|
+
`[SmartCache] Skipping ${operation} due to memory pressure`
|
|
572
|
+
));
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
404
576
|
/**
|
|
405
577
|
* Load persistent cache from disk
|
|
406
578
|
* @private
|
|
@@ -464,6 +636,34 @@ class SmartCache {
|
|
|
464
636
|
*/
|
|
465
637
|
savePersistentCache() {
|
|
466
638
|
if (!this.options.enablePersistence) return;
|
|
639
|
+
|
|
640
|
+
// Prevent concurrent saves
|
|
641
|
+
if (this.saveInProgress) {
|
|
642
|
+
this.pendingSave = true;
|
|
643
|
+
if (this.options.forceDebug) {
|
|
644
|
+
console.log(formatLogMessage('debug', '[SmartCache] Save in progress, marking pending...'));
|
|
645
|
+
}
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Debounce saves - don't save more than once every 10 seconds
|
|
650
|
+
const now = Date.now();
|
|
651
|
+
if (now - this.lastSaveTime < 10000) {
|
|
652
|
+
// Schedule a delayed save if none is pending
|
|
653
|
+
if (!this.saveTimeout && !this.pendingSave) {
|
|
654
|
+
this.pendingSave = true;
|
|
655
|
+
this.saveTimeout = setTimeout(() => {
|
|
656
|
+
this.saveTimeout = null;
|
|
657
|
+
if (this.pendingSave) {
|
|
658
|
+
this.pendingSave = false;
|
|
659
|
+
this.savePersistentCache();
|
|
660
|
+
}
|
|
661
|
+
}, 10000 - (now - this.lastSaveTime));
|
|
662
|
+
}
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
this.saveInProgress = true;
|
|
666
|
+
this.lastSaveTime = now;
|
|
467
667
|
|
|
468
668
|
const cacheDir = this.options.persistencePath;
|
|
469
669
|
const cacheFile = path.join(cacheDir, 'smart-cache.json');
|
|
@@ -475,7 +675,7 @@ class SmartCache {
|
|
|
475
675
|
}
|
|
476
676
|
|
|
477
677
|
const data = {
|
|
478
|
-
timestamp:
|
|
678
|
+
timestamp: now,
|
|
479
679
|
domainCache: Array.from(this.domainCache.entries()),
|
|
480
680
|
netToolsCache: Array.from(this.netToolsCache.entries()),
|
|
481
681
|
stats: this.stats
|
|
@@ -495,6 +695,14 @@ class SmartCache {
|
|
|
495
695
|
`[SmartCache] Failed to save cache: ${err.message}`
|
|
496
696
|
));
|
|
497
697
|
}
|
|
698
|
+
} finally {
|
|
699
|
+
this.saveInProgress = false;
|
|
700
|
+
|
|
701
|
+
// Process any pending saves
|
|
702
|
+
if (this.pendingSave && !this.saveTimeout) {
|
|
703
|
+
this.pendingSave = false;
|
|
704
|
+
setTimeout(() => this.savePersistentCache(), 1000);
|
|
705
|
+
}
|
|
498
706
|
}
|
|
499
707
|
}
|
|
500
708
|
|
|
@@ -512,9 +720,16 @@ class SmartCache {
|
|
|
512
720
|
* Clean up resources
|
|
513
721
|
*/
|
|
514
722
|
destroy() {
|
|
723
|
+
if (this.memoryCheckInterval) {
|
|
724
|
+
clearInterval(this.memoryCheckInterval);
|
|
725
|
+
}
|
|
515
726
|
if (this.autoSaveInterval) {
|
|
516
727
|
clearInterval(this.autoSaveInterval);
|
|
517
728
|
}
|
|
729
|
+
if (this.saveTimeout) {
|
|
730
|
+
clearTimeout(this.saveTimeout);
|
|
731
|
+
this.saveTimeout = null;
|
|
732
|
+
}
|
|
518
733
|
|
|
519
734
|
// Save cache one last time
|
|
520
735
|
if (this.options.enablePersistence) {
|
|
@@ -523,6 +738,92 @@ class SmartCache {
|
|
|
523
738
|
|
|
524
739
|
this.clear();
|
|
525
740
|
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Clear persistent cache files and directories
|
|
744
|
+
* @param {Object} options - Clear options
|
|
745
|
+
* @param {boolean} options.silent - Suppress console output
|
|
746
|
+
* @param {boolean} options.forceDebug - Enable debug logging
|
|
747
|
+
* @returns {Object} Clear operation results
|
|
748
|
+
*/
|
|
749
|
+
static clearPersistentCache(options = {}) {
|
|
750
|
+
const { silent = false, forceDebug = false, cachePath = '.cache' } = options;
|
|
751
|
+
|
|
752
|
+
const cachePaths = [
|
|
753
|
+
cachePath,
|
|
754
|
+
path.join(cachePath, 'smart-cache.json'),
|
|
755
|
+
// Add other potential cache files here if needed
|
|
756
|
+
];
|
|
757
|
+
|
|
758
|
+
let clearedItems = 0;
|
|
759
|
+
let totalSize = 0;
|
|
760
|
+
const clearedFiles = [];
|
|
761
|
+
const errors = [];
|
|
762
|
+
|
|
763
|
+
if (!silent) {
|
|
764
|
+
console.log(`\n??? Clearing cache...`);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
for (const currentCachePath of cachePaths) {
|
|
768
|
+
if (fs.existsSync(currentCachePath)) {
|
|
769
|
+
try {
|
|
770
|
+
const stats = fs.statSync(currentCachePath);
|
|
771
|
+
if (stats.isDirectory()) {
|
|
772
|
+
// Calculate total size of directory contents
|
|
773
|
+
const files = fs.readdirSync(currentCachePath);
|
|
774
|
+
for (const file of files) {
|
|
775
|
+
const filePath = path.join(currentCachePath, file);
|
|
776
|
+
if (fs.existsSync(filePath)) {
|
|
777
|
+
totalSize += fs.statSync(filePath).size;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
fs.rmSync(currentCachePath, { recursive: true, force: true });
|
|
781
|
+
clearedItems++;
|
|
782
|
+
clearedFiles.push({ type: 'directory', path: currentCachePath, size: totalSize });
|
|
783
|
+
if (forceDebug) {
|
|
784
|
+
console.log(formatLogMessage('debug', `Cleared cache directory: ${currentCachePath}`));
|
|
785
|
+
}
|
|
786
|
+
} else {
|
|
787
|
+
totalSize += stats.size;
|
|
788
|
+
fs.unlinkSync(currentCachePath);
|
|
789
|
+
clearedItems++;
|
|
790
|
+
clearedFiles.push({ type: 'file', path: currentCachePath, size: stats.size });
|
|
791
|
+
if (forceDebug) {
|
|
792
|
+
console.log(formatLogMessage('debug', `Cleared cache file: ${currentCachePath}`));
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
} catch (clearErr) {
|
|
796
|
+
errors.push({ path: currentCachePath, error: clearErr.message });
|
|
797
|
+
if (forceDebug) {
|
|
798
|
+
console.log(formatLogMessage('debug', `Failed to clear ${currentCachePath}: ${clearErr.message}`));
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const result = {
|
|
805
|
+
success: errors.length === 0,
|
|
806
|
+
clearedItems,
|
|
807
|
+
totalSize,
|
|
808
|
+
sizeMB: (totalSize / 1024 / 1024).toFixed(2),
|
|
809
|
+
clearedFiles,
|
|
810
|
+
errors
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
if (!silent) {
|
|
814
|
+
if (clearedItems > 0) {
|
|
815
|
+
console.log(`? Cache cleared: ${clearedItems} item(s), ${result.sizeMB}MB freed`);
|
|
816
|
+
} else {
|
|
817
|
+
console.log(`?? No cache files found to clear`);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
if (errors.length > 0) {
|
|
821
|
+
console.warn(`?? ${errors.length} error(s) occurred during cache clearing`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
return result;
|
|
826
|
+
}
|
|
526
827
|
}
|
|
527
828
|
|
|
528
829
|
/**
|
|
@@ -541,11 +842,16 @@ function createSmartCache(config = {}) {
|
|
|
541
842
|
persistencePath: config.cache_path || '.cache',
|
|
542
843
|
forceDebug: config.forceDebug || false,
|
|
543
844
|
autoSave: config.cache_autosave !== false,
|
|
544
|
-
autoSaveInterval: (config.cache_autosave_minutes || 1) * 60 * 1000
|
|
845
|
+
autoSaveInterval: (config.cache_autosave_minutes || 1) * 60 * 1000,
|
|
846
|
+
maxHeapUsage: config.cache_max_heap_mb ? config.cache_max_heap_mb * 1024 * 1024 : undefined,
|
|
847
|
+
memoryCheckInterval: (config.cache_memory_check_seconds || 30) * 1000,
|
|
848
|
+
concurrency: config.max_concurrent_sites || 6,
|
|
849
|
+
aggressiveMode: config.cache_aggressive_mode === true
|
|
545
850
|
});
|
|
546
851
|
}
|
|
547
852
|
|
|
548
853
|
module.exports = {
|
|
549
854
|
SmartCache,
|
|
550
|
-
createSmartCache
|
|
855
|
+
createSmartCache,
|
|
856
|
+
clearPersistentCache: SmartCache.clearPersistentCache
|
|
551
857
|
};
|
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v1.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v1.0.58 ===
|
|
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
|
|
@@ -34,13 +34,14 @@ const { performPageInteraction, createInteractionConfig } = require('./lib/inter
|
|
|
34
34
|
// Domain detection cache for performance optimization
|
|
35
35
|
const { createGlobalHelpers, getTotalDomainsSkipped, getDetectedDomainsCount } = require('./lib/domain-cache');
|
|
36
36
|
const { createSmartCache } = require('./lib/smart-cache'); // Smart cache system
|
|
37
|
+
const { clearPersistentCache } = require('./lib/smart-cache');
|
|
37
38
|
// Enhanced redirect handling
|
|
38
39
|
const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/redirect');
|
|
39
40
|
// Ensure web browser is working correctly
|
|
40
41
|
const { monitorBrowserHealth, isBrowserHealthy } = require('./lib/browserhealth');
|
|
41
42
|
|
|
42
43
|
// --- Script Configuration & Constants ---
|
|
43
|
-
const VERSION = '1.0.
|
|
44
|
+
const VERSION = '1.0.58'; // Script version
|
|
44
45
|
|
|
45
46
|
// get startTime
|
|
46
47
|
const startTime = Date.now();
|
|
@@ -102,6 +103,8 @@ const validateConfig = args.includes('--validate-config');
|
|
|
102
103
|
const validateRules = args.includes('--validate-rules');
|
|
103
104
|
const testValidation = args.includes('--test-validation');
|
|
104
105
|
let cleanRules = args.includes('--clean-rules');
|
|
106
|
+
const clearCache = args.includes('--clear-cache');
|
|
107
|
+
const ignoreCache = args.includes('--ignore-cache');
|
|
105
108
|
|
|
106
109
|
let validateRulesFile = null;
|
|
107
110
|
const validateRulesIndex = args.findIndex(arg => arg === '--validate-rules');
|
|
@@ -224,6 +227,15 @@ if (args.includes('--version')) {
|
|
|
224
227
|
process.exit(0);
|
|
225
228
|
}
|
|
226
229
|
|
|
230
|
+
// Handle --clear-cache before config loading (uses default cache path)
|
|
231
|
+
if (clearCache && !dryRunMode) {
|
|
232
|
+
clearPersistentCache({
|
|
233
|
+
silent: silentMode,
|
|
234
|
+
forceDebug,
|
|
235
|
+
cachePath: '.cache' // Default path, will be updated after config loads if needed
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
227
239
|
// Handle validation-only operations before main help
|
|
228
240
|
if (testValidation) {
|
|
229
241
|
console.log(`\n${messageColors.processing('Running domain validation tests...')}`);
|
|
@@ -360,6 +372,8 @@ Validation Options:
|
|
|
360
372
|
--validate-rules [file] Validate rule file format (uses --output/--compare files if no file specified)
|
|
361
373
|
--clean-rules [file] Clean rule files by removing invalid lines and optionally duplicates (uses --output/--compare files if no file specified)
|
|
362
374
|
--test-validation Run domain validation tests and exit
|
|
375
|
+
--clear-cache Clear persistent cache before scanning (improves fresh start performance)
|
|
376
|
+
--ignore-cache Bypass all smart caching functionality during scanning
|
|
363
377
|
|
|
364
378
|
Global config.json options:
|
|
365
379
|
ignoreDomains: ["domain.com", "*.ads.com"] Domains to completely ignore (supports wildcards)
|
|
@@ -551,15 +565,40 @@ const RESOURCE_CLEANUP_INTERVAL = (() => {
|
|
|
551
565
|
return 180;
|
|
552
566
|
})();
|
|
553
567
|
|
|
554
|
-
//
|
|
568
|
+
// Perform cache clear after config is loaded for custom cache paths
|
|
569
|
+
if (clearCache && dryRunMode) {
|
|
570
|
+
clearPersistentCache({
|
|
571
|
+
silent: silentMode,
|
|
572
|
+
forceDebug,
|
|
573
|
+
cachePath: config.cache_path || '.cache'
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Also clear for custom cache paths in normal mode if not already cleared
|
|
578
|
+
if (clearCache && !dryRunMode && config.cache_path && config.cache_path !== '.cache') {
|
|
579
|
+
clearPersistentCache({
|
|
580
|
+
silent: silentMode,
|
|
581
|
+
forceDebug,
|
|
582
|
+
cachePath: config.cache_path
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Initialize smart cache system AFTER config is loaded (unless --ignore-cache is used)
|
|
587
|
+
if (ignoreCache) {
|
|
588
|
+
smartCache = null;
|
|
589
|
+
if (forceDebug) console.log(formatLogMessage('debug', 'Smart cache disabled by --ignore-cache flag'));
|
|
590
|
+
} else {
|
|
555
591
|
smartCache = createSmartCache({
|
|
556
592
|
...config,
|
|
557
593
|
forceDebug,
|
|
558
|
-
|
|
559
|
-
|
|
594
|
+
max_concurrent_sites: MAX_CONCURRENT_SITES, // Pass concurrency info
|
|
595
|
+
cache_aggressive_mode: MAX_CONCURRENT_SITES > 12, // Auto-enable for high concurrency
|
|
596
|
+
cache_persistence: false, // Disable persistence completely
|
|
597
|
+
cache_autosave: false, // Disable auto-save completely
|
|
560
598
|
cache_autosave_minutes: config.cache_autosave_minutes || 1,
|
|
561
599
|
cache_max_size: config.cache_max_size || 5000
|
|
562
600
|
});
|
|
601
|
+
}
|
|
563
602
|
|
|
564
603
|
// Handle --clean-rules after config is loaded (so we have access to sites)
|
|
565
604
|
if (cleanRules || cleanRulesFile) {
|
|
@@ -1503,7 +1542,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1503
1542
|
const similarityThreshold = siteConfig.ignore_similar_threshold || ignore_similar_threshold;
|
|
1504
1543
|
const ignoreSimilarIgnoredDomains = siteConfig.ignore_similar_ignored_domains !== undefined ? siteConfig.ignore_similar_ignored_domains : ignore_similar_ignored_domains;
|
|
1505
1544
|
|
|
1506
|
-
// Use smart cache's similarity cache for performance
|
|
1545
|
+
// Use smart cache's similarity cache for performance (if cache is enabled)
|
|
1507
1546
|
if (ignoreSimilarEnabled && smartCache) {
|
|
1508
1547
|
const existingDomains = matchedDomains instanceof Map
|
|
1509
1548
|
? Array.from(matchedDomains.keys()).filter(key => !['dryRunMatches', 'dryRunNetTools', 'dryRunSearchString'].includes(key))
|
|
@@ -1522,14 +1561,14 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1522
1561
|
// If no cached similarity exists, calculate and cache it
|
|
1523
1562
|
if (cachedSimilarity === null) {
|
|
1524
1563
|
const similarity = calculateSimilarity(domain, existingDomain);
|
|
1525
|
-
if (smartCache) {
|
|
1564
|
+
if (smartCache && !ignoreCache) {
|
|
1526
1565
|
smartCache.cacheSimilarity(domain, existingDomain, similarity);
|
|
1527
1566
|
}
|
|
1528
1567
|
}
|
|
1529
1568
|
}
|
|
1530
1569
|
}
|
|
1531
1570
|
|
|
1532
|
-
// Check smart cache first
|
|
1571
|
+
// Check smart cache first (if cache is enabled)
|
|
1533
1572
|
const context = {
|
|
1534
1573
|
filterRegex: siteConfig.filterRegex,
|
|
1535
1574
|
searchString: siteConfig.searchstring,
|
|
@@ -1581,7 +1620,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1581
1620
|
// Mark full subdomain as detected for future reference
|
|
1582
1621
|
markDomainAsDetected(cacheKey);
|
|
1583
1622
|
|
|
1584
|
-
// Also mark in smart cache with context
|
|
1623
|
+
// Also mark in smart cache with context (if cache is enabled)
|
|
1585
1624
|
if (smartCache) {
|
|
1586
1625
|
smartCache.markDomainProcessed(domain, context, { resourceType, fullSubdomain });
|
|
1587
1626
|
}
|
|
@@ -1831,7 +1870,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1831
1870
|
}
|
|
1832
1871
|
|
|
1833
1872
|
// Create and execute nettools handler
|
|
1834
|
-
// Check smart cache for nettools results
|
|
1873
|
+
// Check smart cache for nettools results (if cache is enabled)
|
|
1835
1874
|
const cachedWhois = smartCache ? smartCache.getCachedNetTools(reqDomain, 'whois') : null;
|
|
1836
1875
|
const cachedDig = smartCache ? smartCache.getCachedNetTools(reqDomain, 'dig', digRecordType) : null;
|
|
1837
1876
|
|
|
@@ -1839,7 +1878,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1839
1878
|
console.log(formatLogMessage('debug', `[SmartCache] Using cached nettools results for ${reqDomain}`));
|
|
1840
1879
|
}
|
|
1841
1880
|
|
|
1842
|
-
// Create nettools handler with cache callbacks
|
|
1881
|
+
// Create nettools handler with cache callbacks (if cache is enabled)
|
|
1843
1882
|
const netToolsHandler = createNetToolsHandler({
|
|
1844
1883
|
whoisTerms,
|
|
1845
1884
|
whoisOrTerms,
|
|
@@ -1857,7 +1896,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1857
1896
|
matchedDomains,
|
|
1858
1897
|
addMatchedDomain,
|
|
1859
1898
|
isDomainAlreadyDetected,
|
|
1860
|
-
// Add cache callbacks if smart cache is available
|
|
1899
|
+
// Add cache callbacks if smart cache is available and caching is enabled
|
|
1861
1900
|
onWhoisResult: smartCache ? (domain, result) => {
|
|
1862
1901
|
smartCache.cacheNetTools(domain, 'whois', result);
|
|
1863
1902
|
} : undefined,
|
|
@@ -1905,7 +1944,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1905
1944
|
|
|
1906
1945
|
// If curl is enabled, download and analyze content immediately
|
|
1907
1946
|
if (useCurl) {
|
|
1908
|
-
// Check response cache first if smart cache is available
|
|
1947
|
+
// Check response cache first if smart cache is available and caching is enabled
|
|
1909
1948
|
const cachedContent = smartCache ? smartCache.getCachedResponse(reqUrl) : null;
|
|
1910
1949
|
|
|
1911
1950
|
if (cachedContent && forceDebug) {
|
|
@@ -1922,7 +1961,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1922
1961
|
matchedDomains,
|
|
1923
1962
|
addMatchedDomain, // Pass the helper function
|
|
1924
1963
|
isDomainAlreadyDetected,
|
|
1925
|
-
onContentFetched: smartCache ? (url, content) => {
|
|
1964
|
+
onContentFetched: smartCache && !ignoreCache ? (url, content) => {
|
|
1926
1965
|
smartCache.cacheResponse(url, content);
|
|
1927
1966
|
} : undefined,
|
|
1928
1967
|
currentUrl,
|
|
@@ -2587,7 +2626,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2587
2626
|
console.log(formatLogMessage('debug', `Output format: ${getFormatDescription(globalOptions)}`));
|
|
2588
2627
|
console.log(formatLogMessage('debug', `Generated ${outputResult.totalRules} rules from ${outputResult.successfulPageLoads} successful page loads`));
|
|
2589
2628
|
console.log(formatLogMessage('debug', `Performance: ${totalDomainsSkipped} domains skipped (already detected), ${detectedDomainsCount} unique domains cached`));
|
|
2590
|
-
// Log smart cache statistics
|
|
2629
|
+
// Log smart cache statistics (if cache is enabled)
|
|
2591
2630
|
if (smartCache) {
|
|
2592
2631
|
const cacheStats = smartCache.getStats();
|
|
2593
2632
|
console.log(formatLogMessage('debug', '=== Smart Cache Statistics ==='));
|
|
@@ -2677,7 +2716,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2677
2716
|
const seconds = totalSeconds % 60;
|
|
2678
2717
|
|
|
2679
2718
|
// Final summary report with timing and success statistics
|
|
2680
|
-
// Clean up smart cache
|
|
2719
|
+
// Clean up smart cache (if it exists)
|
|
2681
2720
|
if (smartCache) {
|
|
2682
2721
|
smartCache.destroy();
|
|
2683
2722
|
}
|
|
@@ -2699,6 +2738,9 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2699
2738
|
if (totalDomainsSkipped > 0) {
|
|
2700
2739
|
console.log(messageColors.info('Performance:') + ` ${totalDomainsSkipped} domains skipped (already detected)`);
|
|
2701
2740
|
}
|
|
2741
|
+
if (ignoreCache && forceDebug) {
|
|
2742
|
+
console.log(messageColors.info('Cache:') + ` Smart caching was disabled`);
|
|
2743
|
+
}
|
|
2702
2744
|
}
|
|
2703
2745
|
|
|
2704
2746
|
// Clean process termination
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.58",
|
|
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": {
|