@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.
@@ -0,0 +1,168 @@
1
+ // === Color Utility Module (colorize.js) ===
2
+ // Centralized color management for nwss.js console output
3
+ // Provides consistent theming across all modules
4
+
5
+ /**
6
+ * Detects if color output should be enabled based on command line arguments
7
+ * @returns {boolean} True if --color or --colour flag is present
8
+ */
9
+ function shouldEnableColors() {
10
+ return process.argv.includes('--color') || process.argv.includes('--colour');
11
+ }
12
+
13
+ // Initialize color support based on command line flags
14
+ const enableColors = shouldEnableColors();
15
+
16
+ /**
17
+ * ANSI color codes object
18
+ * Only contains actual escape sequences if colors are enabled
19
+ */
20
+ const colors = {
21
+ // Reset and formatting
22
+ reset: enableColors ? '\x1b[0m' : '',
23
+ bright: enableColors ? '\x1b[1m' : '',
24
+ dim: enableColors ? '\x1b[2m' : '',
25
+
26
+ // Standard colors
27
+ red: enableColors ? '\x1b[31m' : '',
28
+ green: enableColors ? '\x1b[32m' : '',
29
+ yellow: enableColors ? '\x1b[33m' : '',
30
+ blue: enableColors ? '\x1b[34m' : '',
31
+ magenta: enableColors ? '\x1b[35m' : '',
32
+ cyan: enableColors ? '\x1b[36m' : '',
33
+ white: enableColors ? '\x1b[37m' : '',
34
+
35
+ // Extended colors
36
+ gray: enableColors ? '\x1b[90m' : '',
37
+ brightRed: enableColors ? '\x1b[91m' : '',
38
+ brightGreen: enableColors ? '\x1b[92m' : '',
39
+ brightYellow: enableColors ? '\x1b[93m' : '',
40
+ brightBlue: enableColors ? '\x1b[94m' : '',
41
+ brightMagenta: enableColors ? '\x1b[95m' : '',
42
+ brightCyan: enableColors ? '\x1b[96m' : '',
43
+ brightWhite: enableColors ? '\x1b[97m' : ''
44
+ };
45
+
46
+ /**
47
+ * Applies color formatting to text if colors are enabled
48
+ * @param {string} text - The text to colorize
49
+ * @param {string} color - The ANSI color code to apply
50
+ * @returns {string} Colored text (or plain text if colors disabled)
51
+ */
52
+ function colorize(text, color) {
53
+ return enableColors ? `${color}${text}${colors.reset}` : text;
54
+ }
55
+
56
+ /**
57
+ * Pre-built color functions for common message types
58
+ * These provide semantic coloring for different types of console output
59
+ */
60
+ const messageColors = {
61
+ // Status and logging
62
+ debug: (text) => colorize(text, colors.gray),
63
+ info: (text) => colorize(text, colors.blue),
64
+ warn: (text) => colorize(text, colors.yellow),
65
+ error: (text) => colorize(text, colors.red),
66
+ success: (text) => colorize(text, colors.green),
67
+
68
+ // Process states
69
+ scanning: (text) => colorize(text, colors.yellow),
70
+ loaded: (text) => colorize(text, colors.green),
71
+ processing: (text) => colorize(text, colors.cyan),
72
+ match: (text) => colorize(text, colors.green),
73
+ blocked: (text) => colorize(text, colors.red),
74
+
75
+ // Special emphasis
76
+ highlight: (text) => colorize(text, colors.brightCyan),
77
+ emphasis: (text) => colorize(text, colors.bright),
78
+ timing: (text) => colorize(text, colors.cyan),
79
+
80
+ // File operations
81
+ fileOp: (text) => colorize(text, colors.magenta),
82
+ compression: (text) => colorize(text, colors.cyan)
83
+ };
84
+
85
+ /**
86
+ * Creates a colored tag with consistent formatting
87
+ * Used for status tags like [debug], [info], [warn], etc.
88
+ * @param {string} tag - The tag text (without brackets)
89
+ * @param {string} color - The color to apply
90
+ * @returns {string} Formatted colored tag
91
+ */
92
+ function createTag(tag, color) {
93
+ return colorize(`[${tag}]`, color);
94
+ }
95
+
96
+ /**
97
+ * Pre-built tags for common log levels
98
+ */
99
+ const tags = {
100
+ debug: createTag('debug', colors.gray),
101
+ info: createTag('info', colors.blue),
102
+ warn: createTag('warn', colors.yellow),
103
+ error: createTag('error', colors.red),
104
+ match: createTag('match', colors.green),
105
+ compare: createTag('compare', colors.blue)
106
+ };
107
+
108
+ /**
109
+ * Formats a complete log message with colored tag and message
110
+ * @param {string} tag - The tag name (debug, info, warn, error, etc.)
111
+ * @param {string} message - The message content
112
+ * @returns {string} Formatted log message
113
+ */
114
+ function formatLogMessage(tag, message) {
115
+ const coloredTag = tags[tag] || createTag(tag, colors.white);
116
+ return `${coloredTag} ${message}`;
117
+ }
118
+
119
+ /**
120
+ * Utility function to check if colors are currently enabled
121
+ * @returns {boolean} Current color enable status
122
+ */
123
+ function isColorEnabled() {
124
+ return enableColors;
125
+ }
126
+
127
+ /**
128
+ * Creates a rainbow effect for special messages (like completion)
129
+ * @param {string} text - Text to apply rainbow effect to
130
+ * @returns {string} Text with rainbow coloring (if colors enabled)
131
+ */
132
+ function rainbow(text) {
133
+ if (!enableColors || text.length === 0) return text;
134
+
135
+ const rainbowColors = [
136
+ colors.red, colors.yellow, colors.green,
137
+ colors.cyan, colors.blue, colors.magenta
138
+ ];
139
+
140
+ return text
141
+ .split('')
142
+ .map((char, index) => {
143
+ const colorIndex = index % rainbowColors.length;
144
+ return `${rainbowColors[colorIndex]}${char}`;
145
+ })
146
+ .join('') + colors.reset;
147
+ }
148
+
149
+ module.exports = {
150
+ // Core functions
151
+ colorize,
152
+ colors,
153
+
154
+ // Semantic coloring
155
+ messageColors,
156
+ tags,
157
+ createTag,
158
+ formatLogMessage,
159
+
160
+ // Utility functions
161
+ isColorEnabled,
162
+ shouldEnableColors,
163
+ rainbow,
164
+
165
+ // Legacy compatibility - keep original function names
166
+ colorize: colorize, // Explicit export for backward compatibility
167
+ colors: colors // Explicit export for backward compatibility
168
+ };
package/lib/compare.js ADDED
@@ -0,0 +1,159 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Loads rules from a comparison file and returns them as a Set for fast lookup
6
+ * @param {string} compareFilePath - Path to the file containing existing rules
7
+ * @param {boolean} forceDebug - Whether to show debug output
8
+ * @returns {Set<string>} Set of existing rules (normalized)
9
+ */
10
+ function loadComparisonRules(compareFilePath, forceDebug = false) {
11
+ try {
12
+ if (!fs.existsSync(compareFilePath)) {
13
+ throw new Error(`Comparison file not found: ${compareFilePath}`);
14
+ }
15
+
16
+ const content = fs.readFileSync(compareFilePath, 'utf8');
17
+ const lines = content.split('\n')
18
+ .map(line => line.trim())
19
+ .filter(line => line && !line.startsWith('!') && !line.startsWith('#')); // Skip comments and empty lines
20
+
21
+ const rules = new Set();
22
+
23
+ for (const line of lines) {
24
+ // Normalize the rule by removing different prefixes/formats
25
+ let normalizedRule = line;
26
+
27
+ // Remove adblock prefixes (||, |, etc.)
28
+ normalizedRule = normalizedRule.replace(/^\|\|/, '');
29
+ normalizedRule = normalizedRule.replace(/^\|/, '');
30
+
31
+ // Remove localhost prefixes
32
+ normalizedRule = normalizedRule.replace(/^127\.0\.0\.1\s+/, '');
33
+ normalizedRule = normalizedRule.replace(/^0\.0\.0\.0\s+/, '');
34
+
35
+ // Remove adblock suffixes and modifiers
36
+ normalizedRule = normalizedRule.replace(/\^.*$/, ''); // Remove ^ and everything after
37
+ normalizedRule = normalizedRule.replace(/\$.*$/, ''); // Remove $ and everything after
38
+
39
+ // Clean up and add to set
40
+ normalizedRule = normalizedRule.trim();
41
+ if (normalizedRule) {
42
+ rules.add(normalizedRule);
43
+ }
44
+ }
45
+
46
+ if (forceDebug) {
47
+ console.log(`[debug] Loaded ${rules.size} comparison rules from ${compareFilePath}`);
48
+ }
49
+
50
+ return rules;
51
+ } catch (error) {
52
+ throw new Error(`Failed to load comparison file: ${error.message}`);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Normalizes a rule to match the format used in comparison
58
+ * @param {string} rule - The rule to normalize
59
+ * @returns {string} Normalized rule
60
+ */
61
+ function normalizeRule(rule) {
62
+ let normalized = rule;
63
+
64
+ // Remove adblock prefixes
65
+ normalized = normalized.replace(/^\|\|/, '');
66
+ normalized = normalized.replace(/^\|/, '');
67
+
68
+ // Remove localhost prefixes
69
+ normalized = normalized.replace(/^127\.0\.0\.1\s+/, '');
70
+ normalized = normalized.replace(/^0\.0\.0\.0\s+/, '');
71
+
72
+ // Remove adblock suffixes and modifiers
73
+ normalized = normalized.replace(/\^.*$/, '');
74
+ normalized = normalized.replace(/\$.*$/, '');
75
+
76
+ return normalized.trim();
77
+ }
78
+
79
+ /**
80
+ * Filters out rules that exist in the comparison set, with smart title handling
81
+ * @param {Array<string>} rules - Array of rules to filter
82
+ * @param {Set<string>} comparisonRules - Set of existing rules
83
+ * @param {boolean} forceDebug - Whether to show debug output
84
+ * @returns {Array<string>} Filtered rules array
85
+ */
86
+ function filterUniqueRules(rules, comparisonRules, forceDebug = false) {
87
+ const result = [];
88
+ let duplicateCount = 0;
89
+ let orphanedTitles = 0;
90
+
91
+ // Group rules by titles for smart filtering
92
+ const groups = [];
93
+ let currentGroup = { title: null, rules: [] };
94
+
95
+ for (const rule of rules) {
96
+ if (rule.startsWith('!')) {
97
+ // Start a new group when we encounter a title
98
+ if (currentGroup.title !== null || currentGroup.rules.length > 0) {
99
+ groups.push(currentGroup);
100
+ }
101
+ currentGroup = { title: rule, rules: [] };
102
+ } else {
103
+ // Add rule to current group
104
+ currentGroup.rules.push(rule);
105
+ }
106
+ }
107
+
108
+ // Don't forget the last group
109
+ if (currentGroup.title !== null || currentGroup.rules.length > 0) {
110
+ groups.push(currentGroup);
111
+ }
112
+
113
+ // Process each group
114
+ for (const group of groups) {
115
+ const filteredRules = [];
116
+
117
+ // Filter rules in this group
118
+ for (const rule of group.rules) {
119
+ const normalized = normalizeRule(rule);
120
+
121
+ if (!comparisonRules.has(normalized)) {
122
+ filteredRules.push(rule);
123
+ } else {
124
+ duplicateCount++;
125
+ if (forceDebug) {
126
+ console.log(`[debug] Filtered duplicate rule: ${rule} (normalized: ${normalized})`);
127
+ }
128
+ }
129
+ }
130
+
131
+ // Only include title if there are remaining rules, or if there's no title (rules without titles)
132
+ if (group.title && filteredRules.length > 0) {
133
+ result.push(group.title);
134
+ result.push(...filteredRules);
135
+ } else if (!group.title && filteredRules.length > 0) {
136
+ // Rules without a title - just add them
137
+ result.push(...filteredRules);
138
+ } else if (group.title && filteredRules.length === 0) {
139
+ // Title with no remaining rules - this is an orphaned title
140
+ orphanedTitles++;
141
+ if (forceDebug) {
142
+ console.log(`[debug] Filtered orphaned title: ${group.title} (no unique rules remaining)`);
143
+ }
144
+ }
145
+ }
146
+
147
+ if (forceDebug) {
148
+ console.log(`[debug] Filtered ${duplicateCount} duplicate rules and ${orphanedTitles} orphaned titles`);
149
+ console.log(`[debug] ${result.filter(r => !r.startsWith('!')).length} unique rules remaining`);
150
+ }
151
+
152
+ return result;
153
+ }
154
+
155
+ module.exports = {
156
+ loadComparisonRules,
157
+ normalizeRule,
158
+ filterUniqueRules
159
+ };
@@ -0,0 +1,129 @@
1
+ // === Log Compression Utility ===
2
+ // This module provides gzip compression functionality for log files
3
+
4
+ const fs = require('fs');
5
+ const zlib = require('zlib');
6
+ const path = require('path');
7
+
8
+ /**
9
+ * Compresses a file using gzip and optionally removes the original
10
+ * @param {string} filePath - Path to the file to compress
11
+ * @param {boolean} removeOriginal - Whether to remove the original file after compression
12
+ * @returns {Promise<string>} - Path to the compressed file
13
+ */
14
+ async function compressFile(filePath, removeOriginal = true) {
15
+ return new Promise((resolve, reject) => {
16
+ const compressedPath = `${filePath}.gz`;
17
+
18
+ // Create read and write streams
19
+ const readStream = fs.createReadStream(filePath);
20
+ const writeStream = fs.createWriteStream(compressedPath);
21
+ const gzipStream = zlib.createGzip();
22
+
23
+ // Handle errors
24
+ const handleError = (error) => {
25
+ // Clean up partial compressed file on error
26
+ try {
27
+ if (fs.existsSync(compressedPath)) {
28
+ fs.unlinkSync(compressedPath);
29
+ }
30
+ } catch (cleanupErr) {
31
+ // Ignore cleanup errors
32
+ }
33
+ reject(error);
34
+ };
35
+
36
+ readStream.on('error', handleError);
37
+ writeStream.on('error', handleError);
38
+ gzipStream.on('error', handleError);
39
+
40
+ // Handle successful completion
41
+ writeStream.on('finish', () => {
42
+ if (removeOriginal) {
43
+ try {
44
+ fs.unlinkSync(filePath);
45
+ } catch (removeErr) {
46
+ // If we can't remove original, still consider compression successful
47
+ console.warn(`[warn] Failed to remove original file ${filePath}: ${removeErr.message}`);
48
+ }
49
+ }
50
+ resolve(compressedPath);
51
+ });
52
+
53
+ // Pipe the streams
54
+ readStream.pipe(gzipStream).pipe(writeStream);
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Compresses multiple files and returns results
60
+ * @param {string[]} filePaths - Array of file paths to compress
61
+ * @param {boolean} removeOriginals - Whether to remove original files
62
+ * @returns {Promise<Object>} - Object with successful and failed compressions
63
+ */
64
+ async function compressMultipleFiles(filePaths, removeOriginals = true) {
65
+ const results = {
66
+ successful: [],
67
+ failed: []
68
+ };
69
+
70
+ for (const filePath of filePaths) {
71
+ try {
72
+ if (fs.existsSync(filePath)) {
73
+ const compressedPath = await compressFile(filePath, removeOriginals);
74
+ results.successful.push({
75
+ original: filePath,
76
+ compressed: compressedPath
77
+ });
78
+ } else {
79
+ results.failed.push({
80
+ path: filePath,
81
+ error: 'File does not exist'
82
+ });
83
+ }
84
+ } catch (error) {
85
+ results.failed.push({
86
+ path: filePath,
87
+ error: error.message
88
+ });
89
+ }
90
+ }
91
+
92
+ return results;
93
+ }
94
+
95
+ /**
96
+ * Gets the compression ratio of a file
97
+ * @param {string} originalPath - Path to original file
98
+ * @param {string} compressedPath - Path to compressed file
99
+ * @returns {number} - Compression ratio (0-1, where 0.5 means 50% of original size)
100
+ */
101
+ function getCompressionRatio(originalPath, compressedPath) {
102
+ try {
103
+ const originalSize = fs.statSync(originalPath).size;
104
+ const compressedSize = fs.statSync(compressedPath).size;
105
+ return compressedSize / originalSize;
106
+ } catch (error) {
107
+ return null;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Formats file size in human readable format
113
+ * @param {number} bytes - Size in bytes
114
+ * @returns {string} - Formatted size string
115
+ */
116
+ function formatFileSize(bytes) {
117
+ if (bytes === 0) return '0 B';
118
+ const k = 1024;
119
+ const sizes = ['B', 'KB', 'MB', 'GB'];
120
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
121
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
122
+ }
123
+
124
+ module.exports = {
125
+ compressFile,
126
+ compressMultipleFiles,
127
+ getCompressionRatio,
128
+ formatFileSize
129
+ };