envprobe 1.0.0
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/LICENSE +21 -0
- package/README.md +316 -0
- package/bin/envcheck.js +68 -0
- package/package.json +49 -0
- package/src/analyzer.js +179 -0
- package/src/autocomplete.js +135 -0
- package/src/cache.js +114 -0
- package/src/cli.js +606 -0
- package/src/config.js +118 -0
- package/src/formatters/github.js +164 -0
- package/src/formatters/json.js +114 -0
- package/src/formatters/table.js +92 -0
- package/src/formatters/text.js +198 -0
- package/src/ignore.js +313 -0
- package/src/parser.js +119 -0
- package/src/plugins.js +138 -0
- package/src/progress.js +181 -0
- package/src/repl.js +416 -0
- package/src/scanner.js +182 -0
- package/src/scanners/go.js +89 -0
- package/src/scanners/javascript.js +93 -0
- package/src/scanners/python.js +97 -0
- package/src/scanners/ruby.js +90 -0
- package/src/scanners/rust.js +103 -0
- package/src/scanners/shell.js +125 -0
- package/src/security.js +411 -0
- package/src/suggestions.js +154 -0
- package/src/utils.js +57 -0
- package/src/watch.js +131 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autocomplete functionality for REPL
|
|
3
|
+
* Provides intelligent tab completion for commands, options, and file paths
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readdirSync, statSync } from 'fs';
|
|
7
|
+
import { join, dirname, basename } from 'path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get autocomplete suggestions for the current input
|
|
11
|
+
*/
|
|
12
|
+
export function getCompletions(line) {
|
|
13
|
+
const trimmed = line.trim();
|
|
14
|
+
|
|
15
|
+
// Command completion
|
|
16
|
+
if (trimmed.startsWith(':') || trimmed.startsWith('.')) {
|
|
17
|
+
return getCommandCompletions(trimmed.slice(1));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Flag completion
|
|
21
|
+
if (trimmed.includes('--')) {
|
|
22
|
+
return getFlagCompletions(trimmed);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Path completion
|
|
26
|
+
return getPathCompletions(trimmed);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get command completions
|
|
31
|
+
*/
|
|
32
|
+
function getCommandCompletions(partial) {
|
|
33
|
+
const commands = [
|
|
34
|
+
'help',
|
|
35
|
+
'exit',
|
|
36
|
+
'quit',
|
|
37
|
+
'history',
|
|
38
|
+
'clear',
|
|
39
|
+
'config',
|
|
40
|
+
'set',
|
|
41
|
+
'get',
|
|
42
|
+
'results',
|
|
43
|
+
'last',
|
|
44
|
+
'watch',
|
|
45
|
+
'save',
|
|
46
|
+
'load',
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
return commands
|
|
50
|
+
.filter(cmd => cmd.startsWith(partial))
|
|
51
|
+
.map(cmd => `:${cmd}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get flag completions
|
|
56
|
+
*/
|
|
57
|
+
function getFlagCompletions(line) {
|
|
58
|
+
const flags = [
|
|
59
|
+
'--env-file',
|
|
60
|
+
'--format',
|
|
61
|
+
'--fail-on',
|
|
62
|
+
'--ignore',
|
|
63
|
+
'--no-color',
|
|
64
|
+
'--quiet',
|
|
65
|
+
'--help',
|
|
66
|
+
'--version',
|
|
67
|
+
'--watch',
|
|
68
|
+
'--config',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const lastWord = line.split(/\s+/).pop();
|
|
72
|
+
|
|
73
|
+
if (!lastWord.startsWith('--')) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return flags.filter(flag => flag.startsWith(lastWord));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get path completions
|
|
82
|
+
*/
|
|
83
|
+
function getPathCompletions(line) {
|
|
84
|
+
try {
|
|
85
|
+
const words = line.split(/\s+/);
|
|
86
|
+
const lastWord = words[words.length - 1] || '.';
|
|
87
|
+
|
|
88
|
+
let dir = '.';
|
|
89
|
+
let prefix = '';
|
|
90
|
+
|
|
91
|
+
if (lastWord.includes('/') || lastWord.includes('\\')) {
|
|
92
|
+
dir = dirname(lastWord);
|
|
93
|
+
prefix = basename(lastWord);
|
|
94
|
+
} else {
|
|
95
|
+
prefix = lastWord;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const entries = readdirSync(dir);
|
|
99
|
+
|
|
100
|
+
return entries
|
|
101
|
+
.filter(entry => entry.startsWith(prefix))
|
|
102
|
+
.map(entry => {
|
|
103
|
+
const fullPath = join(dir, entry);
|
|
104
|
+
try {
|
|
105
|
+
const isDir = statSync(fullPath).isDirectory();
|
|
106
|
+
return isDir ? `${entry}/` : entry;
|
|
107
|
+
} catch {
|
|
108
|
+
return entry;
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
.slice(0, 20); // Limit to 20 suggestions
|
|
112
|
+
} catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Setup readline autocomplete
|
|
119
|
+
*/
|
|
120
|
+
export function setupAutocomplete(rl) {
|
|
121
|
+
rl.on('line', (line) => {
|
|
122
|
+
// Store in history
|
|
123
|
+
if (line.trim()) {
|
|
124
|
+
rl.history.unshift(line);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Custom completer function
|
|
129
|
+
const completer = (line) => {
|
|
130
|
+
const completions = getCompletions(line);
|
|
131
|
+
return [completions, line];
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return completer;
|
|
135
|
+
}
|
package/src/cache.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caching layer for improved performance
|
|
3
|
+
* Caches file scan results and analysis to speed up repeated runs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createHash } from 'crypto';
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { tmpdir } from 'os';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Cache manager
|
|
13
|
+
*/
|
|
14
|
+
export class Cache {
|
|
15
|
+
constructor(namespace = 'envcheck') {
|
|
16
|
+
this.namespace = namespace;
|
|
17
|
+
this.cacheDir = join(tmpdir(), namespace);
|
|
18
|
+
this.ensureCacheDir();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
ensureCacheDir() {
|
|
22
|
+
if (!existsSync(this.cacheDir)) {
|
|
23
|
+
mkdirSync(this.cacheDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getCacheKey(data) {
|
|
28
|
+
const hash = createHash('sha256');
|
|
29
|
+
hash.update(JSON.stringify(data));
|
|
30
|
+
return hash.digest('hex');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getCachePath(key) {
|
|
34
|
+
return join(this.cacheDir, `${key}.json`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get(key, maxAge = 3600000) {
|
|
38
|
+
try {
|
|
39
|
+
const cacheKey = this.getCacheKey(key);
|
|
40
|
+
const cachePath = this.getCachePath(cacheKey);
|
|
41
|
+
|
|
42
|
+
if (!existsSync(cachePath)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const stats = statSync(cachePath);
|
|
47
|
+
const age = Date.now() - stats.mtimeMs;
|
|
48
|
+
|
|
49
|
+
if (age > maxAge) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const content = readFileSync(cachePath, 'utf-8');
|
|
54
|
+
return JSON.parse(content);
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
set(key, value) {
|
|
61
|
+
try {
|
|
62
|
+
const cacheKey = this.getCacheKey(key);
|
|
63
|
+
const cachePath = this.getCachePath(cacheKey);
|
|
64
|
+
const content = JSON.stringify(value);
|
|
65
|
+
writeFileSync(cachePath, content, 'utf-8');
|
|
66
|
+
return true;
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
clear() {
|
|
73
|
+
try {
|
|
74
|
+
const { readdirSync, unlinkSync } = require('fs');
|
|
75
|
+
const files = readdirSync(this.cacheDir);
|
|
76
|
+
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
unlinkSync(join(this.cacheDir, file));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
has(key) {
|
|
88
|
+
const cacheKey = this.getCacheKey(key);
|
|
89
|
+
const cachePath = this.getCachePath(cacheKey);
|
|
90
|
+
return existsSync(cachePath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Create a cached version of a function
|
|
96
|
+
*/
|
|
97
|
+
export function cached(fn, options = {}) {
|
|
98
|
+
const cache = new Cache(options.namespace);
|
|
99
|
+
const maxAge = options.maxAge || 3600000;
|
|
100
|
+
|
|
101
|
+
return async function (...args) {
|
|
102
|
+
const cacheKey = { fn: fn.name, args };
|
|
103
|
+
|
|
104
|
+
const cached = cache.get(cacheKey, maxAge);
|
|
105
|
+
if (cached !== null) {
|
|
106
|
+
return cached;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const result = await fn(...args);
|
|
110
|
+
cache.set(cacheKey, result);
|
|
111
|
+
|
|
112
|
+
return result;
|
|
113
|
+
};
|
|
114
|
+
}
|