@claude-agent/envcheck 1.4.0 → 1.5.1
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/README.md +121 -18
- package/bin/cli.js +45 -3
- package/package.json +11 -3
- package/src/index.js +277 -1
- package/types/index.d.ts +431 -0
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@claude-agent/envcheck)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
|
-
> Validate .env files, compare with .env.example, find missing or empty variables.
|
|
7
|
+
> Validate .env files, compare with .env.example, find missing or empty variables. **Now with monorepo support!**
|
|
8
8
|
|
|
9
|
-
Never deploy with missing environment variables again.
|
|
9
|
+
Never deploy with missing environment variables again. Works across entire monorepos with a single command.
|
|
10
10
|
|
|
11
11
|
**Built autonomously by [Claude](https://claude.ai)** - an AI assistant by Anthropic.
|
|
12
12
|
|
|
@@ -34,6 +34,9 @@ envcheck .env.production
|
|
|
34
34
|
# Require specific variables
|
|
35
35
|
envcheck -r "DATABASE_URL,API_KEY"
|
|
36
36
|
|
|
37
|
+
# Scan entire monorepo
|
|
38
|
+
envcheck monorepo
|
|
39
|
+
|
|
37
40
|
# Compare two files
|
|
38
41
|
envcheck compare .env .env.staging
|
|
39
42
|
|
|
@@ -101,6 +104,104 @@ envcheck list .env -j
|
|
|
101
104
|
envcheck get .env DATABASE_URL
|
|
102
105
|
```
|
|
103
106
|
|
|
107
|
+
### Monorepo Command
|
|
108
|
+
|
|
109
|
+
Scan all apps and packages in a monorepo with a single command:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Scan from current directory
|
|
113
|
+
envcheck monorepo
|
|
114
|
+
|
|
115
|
+
# Scan specific directory
|
|
116
|
+
envcheck monorepo ./my-monorepo
|
|
117
|
+
|
|
118
|
+
# With verbose output (shows all issues per app)
|
|
119
|
+
envcheck monorepo --verbose
|
|
120
|
+
|
|
121
|
+
# JSON output for CI/CD
|
|
122
|
+
envcheck monorepo --json
|
|
123
|
+
|
|
124
|
+
# Enable secret detection
|
|
125
|
+
envcheck monorepo --secrets
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Monorepo Support
|
|
129
|
+
|
|
130
|
+
envcheck can scan entire monorepos, validating environment variables across all apps and packages.
|
|
131
|
+
|
|
132
|
+
### Supported Structures
|
|
133
|
+
|
|
134
|
+
envcheck automatically detects these common monorepo patterns:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
my-monorepo/
|
|
138
|
+
├── apps/
|
|
139
|
+
│ ├── web/ # ✓ Scanned
|
|
140
|
+
│ │ ├── .env
|
|
141
|
+
│ │ └── .env.example
|
|
142
|
+
│ └── api/ # ✓ Scanned
|
|
143
|
+
│ ├── .env
|
|
144
|
+
│ └── .env.example
|
|
145
|
+
├── packages/
|
|
146
|
+
│ ├── shared/ # ○ Skipped (no .env.example)
|
|
147
|
+
│ └── utils/ # ✓ Scanned
|
|
148
|
+
│ ├── .env
|
|
149
|
+
│ └── .env.example
|
|
150
|
+
└── .env.example # ✓ Root included if exists
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Supported directories: `apps/`, `packages/`, `workspaces/`, `services/`, `libs/`
|
|
154
|
+
|
|
155
|
+
### Example Output
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
$ envcheck monorepo
|
|
159
|
+
|
|
160
|
+
Monorepo Environment Check
|
|
161
|
+
Root: /path/to/monorepo
|
|
162
|
+
|
|
163
|
+
✓ apps/web: passed
|
|
164
|
+
✗ apps/api: 1 error(s)
|
|
165
|
+
○ packages/shared: skipped (No .env.example found)
|
|
166
|
+
✓ packages/utils: passed
|
|
167
|
+
|
|
168
|
+
Summary: 4 apps scanned
|
|
169
|
+
✓ 2 passed
|
|
170
|
+
✗ 1 failed
|
|
171
|
+
○ 1 skipped
|
|
172
|
+
|
|
173
|
+
✗ 1 error(s), 0 warning(s)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Consistency Checks
|
|
177
|
+
|
|
178
|
+
envcheck can detect inconsistencies across apps:
|
|
179
|
+
|
|
180
|
+
- **Shared variables** - Track which variables appear in multiple apps
|
|
181
|
+
- **Type mismatches** - Detect when the same variable has different type hints in different apps
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
const { scanMonorepo } = require('@claude-agent/envcheck');
|
|
185
|
+
|
|
186
|
+
const result = scanMonorepo('.', { checkConsistency: true });
|
|
187
|
+
|
|
188
|
+
console.log(result.consistency.sharedVars);
|
|
189
|
+
// { API_URL: ['apps/web', 'apps/api'] }
|
|
190
|
+
|
|
191
|
+
console.log(result.consistency.mismatches);
|
|
192
|
+
// [{ variable: 'API_URL', issue: 'type_mismatch', details: [...] }]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### GitHub Action for Monorepos
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
- name: Validate all environment files
|
|
199
|
+
uses: claude-agent-tools/envcheck@v1
|
|
200
|
+
with:
|
|
201
|
+
monorepo: 'true'
|
|
202
|
+
strict: 'true'
|
|
203
|
+
```
|
|
204
|
+
|
|
104
205
|
## API Usage
|
|
105
206
|
|
|
106
207
|
```javascript
|
|
@@ -357,22 +458,24 @@ WITH_EQUALS=postgres://user:pass@host/db?opt=val
|
|
|
357
458
|
- **Auto-detection** - Finds .env.example automatically
|
|
358
459
|
- **CI-friendly** - Exit codes and JSON output
|
|
359
460
|
- **Comprehensive** - Parse, validate, compare, generate
|
|
360
|
-
- **
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
|
368
|
-
|
|
|
369
|
-
| **
|
|
370
|
-
| **
|
|
371
|
-
|
|
|
372
|
-
|
|
|
373
|
-
|
|
|
374
|
-
|
|
375
|
-
|
|
461
|
+
- **Monorepo support** - Scan all apps/packages in one command
|
|
462
|
+
- **Well-tested** - 87 tests covering edge cases
|
|
463
|
+
|
|
464
|
+
## vs. dotenv-safe / envalid / dotenv-mono
|
|
465
|
+
|
|
466
|
+
| Feature | envcheck | dotenv-safe | envalid | dotenv-mono |
|
|
467
|
+
|---------|----------|-------------|---------|-------------|
|
|
468
|
+
| Validates presence | ✅ | ✅ | ✅ | ❌ |
|
|
469
|
+
| Based on .env.example | ✅ | ✅ | ❌ (schema) | ❌ |
|
|
470
|
+
| **Static validation** | ✅ | ❌ | ❌ | ❌ |
|
|
471
|
+
| **CI/CD integration** | ✅ GitHub Action | ❌ | ❌ | ❌ |
|
|
472
|
+
| **Pre-commit hook** | ✅ | ❌ | ❌ | ❌ |
|
|
473
|
+
| Type validation | ✅ (static) | ❌ | ✅ (runtime) | ❌ |
|
|
474
|
+
| **Secret detection** | ✅ | ❌ | ❌ | ❌ |
|
|
475
|
+
| **Monorepo scan** | ✅ | ❌ | ❌ | ❌ |
|
|
476
|
+
| Zero dependencies | ✅ | ❌ | ❌ | ❌ |
|
|
477
|
+
|
|
478
|
+
**Key difference:** envcheck validates *before* deployment (shift-left), while dotenv-safe and envalid validate at runtime when your app starts. dotenv-mono helps load env vars in monorepos but doesn't validate them. envcheck is the only tool that validates across entire monorepos with a single command.
|
|
376
479
|
|
|
377
480
|
## License
|
|
378
481
|
|
package/bin/cli.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const { check, compare, validate, list, get, readEnvFile } = require('../src/index.js');
|
|
4
|
+
const { check, compare, validate, list, get, readEnvFile, scanMonorepo, formatMonorepoResult } = require('../src/index.js');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
|
|
8
|
-
const VERSION = '1.
|
|
8
|
+
const VERSION = '1.5.0';
|
|
9
9
|
|
|
10
10
|
const HELP = `
|
|
11
11
|
envcheck - Validate .env files
|
|
12
12
|
|
|
13
13
|
USAGE
|
|
14
14
|
envcheck [options] [file]
|
|
15
|
+
envcheck monorepo [directory]
|
|
15
16
|
envcheck compare <env> <example>
|
|
16
17
|
|
|
17
18
|
COMMANDS
|
|
18
19
|
check (default) Check .env file, optionally against .env.example
|
|
20
|
+
monorepo Scan all apps/packages in a monorepo
|
|
19
21
|
compare Compare two env files
|
|
20
22
|
list List variables in a file
|
|
21
23
|
get <key> Get a specific variable value
|
|
@@ -26,6 +28,8 @@ OPTIONS
|
|
|
26
28
|
--no-empty Warn on empty values
|
|
27
29
|
--no-extra Error on variables not in example
|
|
28
30
|
--strict Treat warnings as errors
|
|
31
|
+
--secrets Enable secret detection (warn about real secrets)
|
|
32
|
+
--verbose Show detailed output (monorepo mode)
|
|
29
33
|
-q, --quiet Only output errors
|
|
30
34
|
-j, --json Output as JSON
|
|
31
35
|
-v, --version Show version
|
|
@@ -35,6 +39,9 @@ EXAMPLES
|
|
|
35
39
|
envcheck Check .env against .env.example
|
|
36
40
|
envcheck .env.production Check specific file
|
|
37
41
|
envcheck -r "API_KEY,DB_URL" Require specific variables
|
|
42
|
+
envcheck monorepo Scan monorepo from current directory
|
|
43
|
+
envcheck monorepo ./my-monorepo Scan specific directory
|
|
44
|
+
envcheck monorepo --verbose Show all issues per app
|
|
38
45
|
envcheck compare .env .env.prod Compare two files
|
|
39
46
|
envcheck list .env List all variables
|
|
40
47
|
envcheck get .env API_KEY Get specific value
|
|
@@ -51,6 +58,8 @@ function parseArgs(args) {
|
|
|
51
58
|
strict: false,
|
|
52
59
|
quiet: false,
|
|
53
60
|
json: false,
|
|
61
|
+
verbose: false,
|
|
62
|
+
detectSecrets: false,
|
|
54
63
|
args: []
|
|
55
64
|
};
|
|
56
65
|
|
|
@@ -78,11 +87,15 @@ function parseArgs(args) {
|
|
|
78
87
|
result.noExtra = true;
|
|
79
88
|
} else if (arg === '--strict') {
|
|
80
89
|
result.strict = true;
|
|
90
|
+
} else if (arg === '--secrets') {
|
|
91
|
+
result.detectSecrets = true;
|
|
92
|
+
} else if (arg === '--verbose') {
|
|
93
|
+
result.verbose = true;
|
|
81
94
|
} else if (arg === '-q' || arg === '--quiet') {
|
|
82
95
|
result.quiet = true;
|
|
83
96
|
} else if (arg === '-j' || arg === '--json') {
|
|
84
97
|
result.json = true;
|
|
85
|
-
} else if (arg === 'compare' || arg === 'list' || arg === 'get') {
|
|
98
|
+
} else if (arg === 'compare' || arg === 'list' || arg === 'get' || arg === 'monorepo') {
|
|
86
99
|
result.command = arg;
|
|
87
100
|
} else if (!arg.startsWith('-')) {
|
|
88
101
|
result.args.push(arg);
|
|
@@ -277,6 +290,32 @@ function runGet(opts) {
|
|
|
277
290
|
return 0;
|
|
278
291
|
}
|
|
279
292
|
|
|
293
|
+
function runMonorepo(opts) {
|
|
294
|
+
const rootDir = opts.args[0] || '.';
|
|
295
|
+
|
|
296
|
+
const result = scanMonorepo(rootDir, {
|
|
297
|
+
noEmpty: opts.noEmpty,
|
|
298
|
+
noExtra: opts.noExtra,
|
|
299
|
+
strict: opts.strict,
|
|
300
|
+
detectSecrets: opts.detectSecrets,
|
|
301
|
+
checkConsistency: true
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
if (opts.json) {
|
|
305
|
+
console.log(JSON.stringify(result, null, 2));
|
|
306
|
+
return result.valid ? 0 : 1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Use formatted output
|
|
310
|
+
const output = formatMonorepoResult(result, {
|
|
311
|
+
colors: !opts.quiet,
|
|
312
|
+
verbose: opts.verbose
|
|
313
|
+
});
|
|
314
|
+
console.log(output);
|
|
315
|
+
|
|
316
|
+
return result.valid ? 0 : 1;
|
|
317
|
+
}
|
|
318
|
+
|
|
280
319
|
function main() {
|
|
281
320
|
const args = process.argv.slice(2);
|
|
282
321
|
const opts = parseArgs(args);
|
|
@@ -287,6 +326,9 @@ function main() {
|
|
|
287
326
|
case 'check':
|
|
288
327
|
exitCode = runCheck(opts);
|
|
289
328
|
break;
|
|
329
|
+
case 'monorepo':
|
|
330
|
+
exitCode = runMonorepo(opts);
|
|
331
|
+
break;
|
|
290
332
|
case 'compare':
|
|
291
333
|
exitCode = runCompare(opts);
|
|
292
334
|
break;
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-agent/envcheck",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Validate .env files, compare with .env.example, find missing or empty variables",
|
|
5
5
|
"main": "src/index.js",
|
|
6
|
+
"types": "types/index.d.ts",
|
|
6
7
|
"bin": {
|
|
7
8
|
"envcheck": "bin/cli.js"
|
|
8
9
|
},
|
|
@@ -22,7 +23,10 @@
|
|
|
22
23
|
"pre-commit",
|
|
23
24
|
"git-hooks",
|
|
24
25
|
"secrets",
|
|
25
|
-
"security"
|
|
26
|
+
"security",
|
|
27
|
+
"monorepo",
|
|
28
|
+
"turborepo",
|
|
29
|
+
"workspaces"
|
|
26
30
|
],
|
|
27
31
|
"author": "Claude Agent <claude-agent@agentmail.to>",
|
|
28
32
|
"license": "MIT",
|
|
@@ -40,6 +44,10 @@
|
|
|
40
44
|
"files": [
|
|
41
45
|
"src/",
|
|
42
46
|
"bin/",
|
|
47
|
+
"types/",
|
|
43
48
|
"pre-commit-hook.sh"
|
|
44
|
-
]
|
|
49
|
+
],
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
}
|
|
45
53
|
}
|
package/src/index.js
CHANGED
|
@@ -683,6 +683,278 @@ function get(filePath, key) {
|
|
|
683
683
|
return env.variables[key];
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
+
/**
|
|
687
|
+
* Find directories that might contain apps/packages in a monorepo
|
|
688
|
+
* @param {string} rootDir - Root directory to scan
|
|
689
|
+
* @returns {string[]} Array of directory paths
|
|
690
|
+
*/
|
|
691
|
+
function findMonorepoApps(rootDir) {
|
|
692
|
+
const apps = [];
|
|
693
|
+
const basePath = path.resolve(rootDir);
|
|
694
|
+
|
|
695
|
+
// Common monorepo patterns
|
|
696
|
+
const patterns = ['apps', 'packages', 'workspaces', 'services', 'libs'];
|
|
697
|
+
|
|
698
|
+
for (const pattern of patterns) {
|
|
699
|
+
const dir = path.join(basePath, pattern);
|
|
700
|
+
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
|
|
701
|
+
// Get all subdirectories
|
|
702
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
703
|
+
for (const entry of entries) {
|
|
704
|
+
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
705
|
+
apps.push(path.join(dir, entry.name));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Also check root for .env.example (some monorepos have root-level env)
|
|
712
|
+
if (fs.existsSync(path.join(basePath, '.env.example')) ||
|
|
713
|
+
fs.existsSync(path.join(basePath, '.env'))) {
|
|
714
|
+
apps.unshift(basePath);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return apps;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Scan a monorepo for env file issues
|
|
722
|
+
* @param {string} rootDir - Root directory of monorepo
|
|
723
|
+
* @param {Object} options - Scan options
|
|
724
|
+
* @returns {Object} Monorepo scan result
|
|
725
|
+
*/
|
|
726
|
+
function scanMonorepo(rootDir, options = {}) {
|
|
727
|
+
const {
|
|
728
|
+
noEmpty = false,
|
|
729
|
+
noExtra = false,
|
|
730
|
+
strict = false,
|
|
731
|
+
checkConsistency = true,
|
|
732
|
+
detectSecrets = false
|
|
733
|
+
} = options;
|
|
734
|
+
|
|
735
|
+
const basePath = path.resolve(rootDir);
|
|
736
|
+
const apps = findMonorepoApps(basePath);
|
|
737
|
+
|
|
738
|
+
const result = {
|
|
739
|
+
root: basePath,
|
|
740
|
+
valid: true,
|
|
741
|
+
apps: [],
|
|
742
|
+
summary: {
|
|
743
|
+
total: apps.length,
|
|
744
|
+
passed: 0,
|
|
745
|
+
failed: 0,
|
|
746
|
+
skipped: 0,
|
|
747
|
+
errors: 0,
|
|
748
|
+
warnings: 0
|
|
749
|
+
},
|
|
750
|
+
consistency: {
|
|
751
|
+
sharedVars: {}, // Variables that appear in multiple apps
|
|
752
|
+
mismatches: [] // Variables with different types/values across apps
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// Track all variables across apps for consistency checking
|
|
757
|
+
const allVars = {}; // { varName: [{ app, value, type }] }
|
|
758
|
+
|
|
759
|
+
for (const appDir of apps) {
|
|
760
|
+
const appName = path.relative(basePath, appDir) || '.';
|
|
761
|
+
const envPath = path.join(appDir, '.env');
|
|
762
|
+
const examplePath = path.join(appDir, '.env.example');
|
|
763
|
+
|
|
764
|
+
const appResult = {
|
|
765
|
+
name: appName,
|
|
766
|
+
path: appDir,
|
|
767
|
+
hasEnv: fs.existsSync(envPath),
|
|
768
|
+
hasExample: fs.existsSync(examplePath),
|
|
769
|
+
valid: true,
|
|
770
|
+
issues: [],
|
|
771
|
+
variables: []
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// Skip if no example file (can't validate)
|
|
775
|
+
if (!appResult.hasExample) {
|
|
776
|
+
appResult.skipped = true;
|
|
777
|
+
appResult.reason = 'No .env.example found';
|
|
778
|
+
result.apps.push(appResult);
|
|
779
|
+
result.summary.skipped++;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// If no .env but has example, that's also a problem
|
|
784
|
+
if (!appResult.hasEnv) {
|
|
785
|
+
appResult.valid = false;
|
|
786
|
+
appResult.issues.push({
|
|
787
|
+
type: 'warning',
|
|
788
|
+
message: 'No .env file found (but .env.example exists)'
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Run check if both files exist
|
|
793
|
+
if (appResult.hasEnv) {
|
|
794
|
+
const checkResult = check(envPath, {
|
|
795
|
+
examplePath,
|
|
796
|
+
noEmpty,
|
|
797
|
+
noExtra,
|
|
798
|
+
strict,
|
|
799
|
+
validateTypes: true,
|
|
800
|
+
detectSecrets
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
appResult.valid = checkResult.valid;
|
|
804
|
+
appResult.issues = checkResult.issues;
|
|
805
|
+
appResult.variables = Object.keys(checkResult.env?.variables || {});
|
|
806
|
+
|
|
807
|
+
// Track variables for consistency check
|
|
808
|
+
if (checkConsistency && checkResult.env?.variables) {
|
|
809
|
+
const example = readEnvFile(examplePath);
|
|
810
|
+
for (const [varName, value] of Object.entries(checkResult.env.variables)) {
|
|
811
|
+
if (!allVars[varName]) {
|
|
812
|
+
allVars[varName] = [];
|
|
813
|
+
}
|
|
814
|
+
allVars[varName].push({
|
|
815
|
+
app: appName,
|
|
816
|
+
value,
|
|
817
|
+
type: example.typeHints?.[varName] || null
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Update summary
|
|
824
|
+
if (appResult.skipped) {
|
|
825
|
+
// Already counted above
|
|
826
|
+
} else if (appResult.valid) {
|
|
827
|
+
result.summary.passed++;
|
|
828
|
+
} else {
|
|
829
|
+
result.summary.failed++;
|
|
830
|
+
result.valid = false;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const errors = appResult.issues.filter(i => i.type === 'error').length;
|
|
834
|
+
const warnings = appResult.issues.filter(i => i.type === 'warning').length;
|
|
835
|
+
result.summary.errors += errors;
|
|
836
|
+
result.summary.warnings += warnings;
|
|
837
|
+
|
|
838
|
+
result.apps.push(appResult);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Check consistency across apps
|
|
842
|
+
if (checkConsistency) {
|
|
843
|
+
for (const [varName, occurrences] of Object.entries(allVars)) {
|
|
844
|
+
if (occurrences.length > 1) {
|
|
845
|
+
result.consistency.sharedVars[varName] = occurrences.map(o => o.app);
|
|
846
|
+
|
|
847
|
+
// Check for type mismatches
|
|
848
|
+
const types = new Set(occurrences.map(o => o.type).filter(Boolean));
|
|
849
|
+
if (types.size > 1) {
|
|
850
|
+
result.consistency.mismatches.push({
|
|
851
|
+
variable: varName,
|
|
852
|
+
issue: 'type_mismatch',
|
|
853
|
+
details: occurrences.map(o => ({ app: o.app, type: o.type }))
|
|
854
|
+
});
|
|
855
|
+
result.valid = false;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
return result;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Format monorepo result for CLI output
|
|
866
|
+
* @param {Object} result - Monorepo scan result
|
|
867
|
+
* @param {Object} options - Format options
|
|
868
|
+
* @returns {string} Formatted output
|
|
869
|
+
*/
|
|
870
|
+
function formatMonorepoResult(result, options = {}) {
|
|
871
|
+
const { colors = true, verbose = false } = options;
|
|
872
|
+
|
|
873
|
+
const c = colors ? {
|
|
874
|
+
green: '\x1b[32m',
|
|
875
|
+
red: '\x1b[31m',
|
|
876
|
+
yellow: '\x1b[33m',
|
|
877
|
+
dim: '\x1b[2m',
|
|
878
|
+
reset: '\x1b[0m',
|
|
879
|
+
bold: '\x1b[1m'
|
|
880
|
+
} : { green: '', red: '', yellow: '', dim: '', reset: '', bold: '' };
|
|
881
|
+
|
|
882
|
+
const lines = [];
|
|
883
|
+
|
|
884
|
+
lines.push(`${c.bold}Monorepo Environment Check${c.reset}`);
|
|
885
|
+
lines.push(`Root: ${result.root}`);
|
|
886
|
+
lines.push('');
|
|
887
|
+
|
|
888
|
+
for (const app of result.apps) {
|
|
889
|
+
const icon = app.skipped ? `${c.dim}○${c.reset}` :
|
|
890
|
+
app.valid ? `${c.green}✓${c.reset}` :
|
|
891
|
+
`${c.red}✗${c.reset}`;
|
|
892
|
+
|
|
893
|
+
let status = '';
|
|
894
|
+
if (app.skipped) {
|
|
895
|
+
status = `${c.dim}skipped (${app.reason})${c.reset}`;
|
|
896
|
+
} else {
|
|
897
|
+
const errors = app.issues.filter(i => i.type === 'error').length;
|
|
898
|
+
const warnings = app.issues.filter(i => i.type === 'warning').length;
|
|
899
|
+
|
|
900
|
+
if (errors === 0 && warnings === 0) {
|
|
901
|
+
status = `${c.green}passed${c.reset}`;
|
|
902
|
+
} else if (errors > 0) {
|
|
903
|
+
status = `${c.red}${errors} error(s)${c.reset}`;
|
|
904
|
+
if (warnings > 0) status += `, ${c.yellow}${warnings} warning(s)${c.reset}`;
|
|
905
|
+
} else {
|
|
906
|
+
status = `${c.yellow}${warnings} warning(s)${c.reset}`;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
lines.push(`${icon} ${app.name}: ${status}`);
|
|
911
|
+
|
|
912
|
+
// Show details in verbose mode
|
|
913
|
+
if (verbose && !app.skipped && app.issues.length > 0) {
|
|
914
|
+
for (const issue of app.issues) {
|
|
915
|
+
const prefix = issue.type === 'error' ? `${c.red} ✗${c.reset}` : `${c.yellow} !${c.reset}`;
|
|
916
|
+
lines.push(`${prefix} ${issue.message}`);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
lines.push('');
|
|
922
|
+
|
|
923
|
+
// Summary
|
|
924
|
+
lines.push(`${c.bold}Summary:${c.reset} ${result.summary.total} apps scanned`);
|
|
925
|
+
if (result.summary.passed > 0) {
|
|
926
|
+
lines.push(` ${c.green}✓${c.reset} ${result.summary.passed} passed`);
|
|
927
|
+
}
|
|
928
|
+
if (result.summary.failed > 0) {
|
|
929
|
+
lines.push(` ${c.red}✗${c.reset} ${result.summary.failed} failed`);
|
|
930
|
+
}
|
|
931
|
+
if (result.summary.skipped > 0) {
|
|
932
|
+
lines.push(` ${c.dim}○${c.reset} ${result.summary.skipped} skipped`);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// Consistency issues
|
|
936
|
+
if (result.consistency.mismatches.length > 0) {
|
|
937
|
+
lines.push('');
|
|
938
|
+
lines.push(`${c.yellow}Consistency Issues:${c.reset}`);
|
|
939
|
+
for (const mismatch of result.consistency.mismatches) {
|
|
940
|
+
lines.push(` ${c.yellow}!${c.reset} ${mismatch.variable}: ${mismatch.issue}`);
|
|
941
|
+
for (const detail of mismatch.details) {
|
|
942
|
+
lines.push(` - ${detail.app}: type=${detail.type || 'unspecified'}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Final status
|
|
948
|
+
lines.push('');
|
|
949
|
+
if (result.valid) {
|
|
950
|
+
lines.push(`${c.green}✓ All checks passed${c.reset}`);
|
|
951
|
+
} else {
|
|
952
|
+
lines.push(`${c.red}✗ ${result.summary.errors} error(s), ${result.summary.warnings} warning(s)${c.reset}`);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
return lines.join('\n');
|
|
956
|
+
}
|
|
957
|
+
|
|
686
958
|
module.exports = {
|
|
687
959
|
parse: parseEnv,
|
|
688
960
|
parseValue,
|
|
@@ -697,5 +969,9 @@ module.exports = {
|
|
|
697
969
|
check,
|
|
698
970
|
generate,
|
|
699
971
|
list,
|
|
700
|
-
get
|
|
972
|
+
get,
|
|
973
|
+
// Monorepo support
|
|
974
|
+
findMonorepoApps,
|
|
975
|
+
scanMonorepo,
|
|
976
|
+
formatMonorepoResult
|
|
701
977
|
};
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for @claude-agent/envcheck
|
|
3
|
+
* Static environment variable validation for Node.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Basic Types
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validation type names supported by envcheck
|
|
12
|
+
*/
|
|
13
|
+
export type ValidationType =
|
|
14
|
+
| 'url'
|
|
15
|
+
| 'port'
|
|
16
|
+
| 'boolean'
|
|
17
|
+
| 'bool'
|
|
18
|
+
| 'email'
|
|
19
|
+
| 'number'
|
|
20
|
+
| 'integer'
|
|
21
|
+
| 'int'
|
|
22
|
+
| 'string'
|
|
23
|
+
| 'str'
|
|
24
|
+
| 'json'
|
|
25
|
+
| 'uuid';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Issue severity level
|
|
29
|
+
*/
|
|
30
|
+
export type IssueType = 'error' | 'warning';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Issue found during validation
|
|
34
|
+
*/
|
|
35
|
+
export interface Issue {
|
|
36
|
+
type: IssueType;
|
|
37
|
+
message: string;
|
|
38
|
+
line?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Result of type validation
|
|
43
|
+
*/
|
|
44
|
+
export interface TypeValidationResult {
|
|
45
|
+
valid: boolean;
|
|
46
|
+
message?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Type validator function
|
|
51
|
+
*/
|
|
52
|
+
export type TypeValidator = (value: string) => TypeValidationResult;
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Parse Types
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Result of parsing a .env file
|
|
60
|
+
*/
|
|
61
|
+
export interface ParseResult {
|
|
62
|
+
variables: Record<string, string>;
|
|
63
|
+
errors: Array<{
|
|
64
|
+
line: number;
|
|
65
|
+
message: string;
|
|
66
|
+
content: string;
|
|
67
|
+
}>;
|
|
68
|
+
warnings: Array<{
|
|
69
|
+
line: number;
|
|
70
|
+
message: string;
|
|
71
|
+
content: string;
|
|
72
|
+
}>;
|
|
73
|
+
lineInfo: Record<string, number>;
|
|
74
|
+
typeHints: Record<string, string>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Result of reading an env file
|
|
79
|
+
*/
|
|
80
|
+
export interface EnvFileResult extends ParseResult {
|
|
81
|
+
exists: boolean;
|
|
82
|
+
path: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Compare Types
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Item missing from env file
|
|
91
|
+
*/
|
|
92
|
+
export interface MissingItem {
|
|
93
|
+
key: string;
|
|
94
|
+
exampleValue: string;
|
|
95
|
+
line: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Extra item in env file
|
|
100
|
+
*/
|
|
101
|
+
export interface ExtraItem {
|
|
102
|
+
key: string;
|
|
103
|
+
value: string;
|
|
104
|
+
line: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Empty item in env file
|
|
109
|
+
*/
|
|
110
|
+
export interface EmptyItem {
|
|
111
|
+
key: string;
|
|
112
|
+
line: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Result of comparing two env files
|
|
117
|
+
*/
|
|
118
|
+
export interface CompareResult {
|
|
119
|
+
env: EnvFileResult;
|
|
120
|
+
example: EnvFileResult;
|
|
121
|
+
missing: MissingItem[];
|
|
122
|
+
extra: ExtraItem[];
|
|
123
|
+
empty: EmptyItem[];
|
|
124
|
+
different: Array<{
|
|
125
|
+
key: string;
|
|
126
|
+
envValue: string;
|
|
127
|
+
exampleValue: string;
|
|
128
|
+
}>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ============================================================================
|
|
132
|
+
// Validate Types
|
|
133
|
+
// ============================================================================
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Options for validate function
|
|
137
|
+
*/
|
|
138
|
+
export interface ValidateOptions {
|
|
139
|
+
/** List of required variable names */
|
|
140
|
+
required?: string[];
|
|
141
|
+
/** Warn on empty values */
|
|
142
|
+
noEmpty?: boolean;
|
|
143
|
+
/** Type specifications for variables */
|
|
144
|
+
types?: Record<string, ValidationType>;
|
|
145
|
+
/** Enable type validation from hints */
|
|
146
|
+
validateTypes?: boolean;
|
|
147
|
+
/** Enable secret detection */
|
|
148
|
+
detectSecrets?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Result of validation
|
|
153
|
+
*/
|
|
154
|
+
export interface ValidateResult {
|
|
155
|
+
valid: boolean;
|
|
156
|
+
env: EnvFileResult;
|
|
157
|
+
issues: Issue[];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Check Types
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Options for check function
|
|
166
|
+
*/
|
|
167
|
+
export interface CheckOptions {
|
|
168
|
+
/** Path to example file */
|
|
169
|
+
examplePath?: string;
|
|
170
|
+
/** List of required variable names */
|
|
171
|
+
required?: string[];
|
|
172
|
+
/** Warn on empty values */
|
|
173
|
+
noEmpty?: boolean;
|
|
174
|
+
/** Error on extra variables */
|
|
175
|
+
noExtra?: boolean;
|
|
176
|
+
/** Treat warnings as errors */
|
|
177
|
+
strict?: boolean;
|
|
178
|
+
/** Type specifications for variables */
|
|
179
|
+
types?: Record<string, ValidationType>;
|
|
180
|
+
/** Enable type validation */
|
|
181
|
+
validateTypes?: boolean;
|
|
182
|
+
/** Enable secret detection */
|
|
183
|
+
detectSecrets?: boolean;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Result of check function
|
|
188
|
+
*/
|
|
189
|
+
export interface CheckResult {
|
|
190
|
+
valid: boolean;
|
|
191
|
+
issues: Issue[];
|
|
192
|
+
summary: {
|
|
193
|
+
errors: number;
|
|
194
|
+
warnings: number;
|
|
195
|
+
};
|
|
196
|
+
env?: EnvFileResult;
|
|
197
|
+
comparison?: CompareResult;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Secret Detection Types
|
|
202
|
+
// ============================================================================
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Secret pattern definition
|
|
206
|
+
*/
|
|
207
|
+
export interface SecretPattern {
|
|
208
|
+
regex: RegExp;
|
|
209
|
+
description: string;
|
|
210
|
+
keyPattern?: RegExp;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Result of secret detection
|
|
215
|
+
*/
|
|
216
|
+
export interface SecretDetectionResult {
|
|
217
|
+
detected: boolean;
|
|
218
|
+
description: string;
|
|
219
|
+
message: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// Monorepo Types
|
|
224
|
+
// ============================================================================
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Options for monorepo scanning
|
|
228
|
+
*/
|
|
229
|
+
export interface ScanMonorepoOptions {
|
|
230
|
+
/** Warn on empty values */
|
|
231
|
+
noEmpty?: boolean;
|
|
232
|
+
/** Error on extra variables */
|
|
233
|
+
noExtra?: boolean;
|
|
234
|
+
/** Treat warnings as errors */
|
|
235
|
+
strict?: boolean;
|
|
236
|
+
/** Check consistency across apps */
|
|
237
|
+
checkConsistency?: boolean;
|
|
238
|
+
/** Enable secret detection */
|
|
239
|
+
detectSecrets?: boolean;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Result for a single app in monorepo scan
|
|
244
|
+
*/
|
|
245
|
+
export interface MonorepoAppResult {
|
|
246
|
+
name: string;
|
|
247
|
+
path: string;
|
|
248
|
+
hasEnv: boolean;
|
|
249
|
+
hasExample: boolean;
|
|
250
|
+
valid: boolean;
|
|
251
|
+
issues: Issue[];
|
|
252
|
+
variables: string[];
|
|
253
|
+
skipped?: boolean;
|
|
254
|
+
reason?: string;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Consistency mismatch in monorepo
|
|
259
|
+
*/
|
|
260
|
+
export interface ConsistencyMismatch {
|
|
261
|
+
variable: string;
|
|
262
|
+
issue: string;
|
|
263
|
+
details: Array<{
|
|
264
|
+
app: string;
|
|
265
|
+
type: string | null;
|
|
266
|
+
value?: string;
|
|
267
|
+
}>;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Result of scanning a monorepo
|
|
272
|
+
*/
|
|
273
|
+
export interface MonorepoScanResult {
|
|
274
|
+
root: string;
|
|
275
|
+
valid: boolean;
|
|
276
|
+
apps: MonorepoAppResult[];
|
|
277
|
+
summary: {
|
|
278
|
+
total: number;
|
|
279
|
+
passed: number;
|
|
280
|
+
failed: number;
|
|
281
|
+
skipped: number;
|
|
282
|
+
errors: number;
|
|
283
|
+
warnings: number;
|
|
284
|
+
};
|
|
285
|
+
consistency: {
|
|
286
|
+
sharedVars: Record<string, string[]>;
|
|
287
|
+
mismatches: ConsistencyMismatch[];
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Options for formatting monorepo result
|
|
293
|
+
*/
|
|
294
|
+
export interface FormatMonorepoOptions {
|
|
295
|
+
/** Enable ANSI colors */
|
|
296
|
+
colors?: boolean;
|
|
297
|
+
/** Show detailed issues */
|
|
298
|
+
verbose?: boolean;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ============================================================================
|
|
302
|
+
// Exported Functions
|
|
303
|
+
// ============================================================================
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Parse .env file content into an object
|
|
307
|
+
* @param content - Raw .env file content
|
|
308
|
+
* @returns Parsed result with variables and metadata
|
|
309
|
+
*/
|
|
310
|
+
export function parse(content: string): ParseResult;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Parse a single value, handling quotes and escapes
|
|
314
|
+
* @param raw - Raw value string
|
|
315
|
+
* @returns Parsed value
|
|
316
|
+
*/
|
|
317
|
+
export function parseValue(raw: string): string;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Read and parse a .env file
|
|
321
|
+
* @param filePath - Path to .env file
|
|
322
|
+
* @returns Parsed file result
|
|
323
|
+
*/
|
|
324
|
+
export function readEnvFile(filePath: string): EnvFileResult;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Compare two env files
|
|
328
|
+
* @param envPath - Path to .env file
|
|
329
|
+
* @param examplePath - Path to .env.example file
|
|
330
|
+
* @returns Comparison result
|
|
331
|
+
*/
|
|
332
|
+
export function compare(envPath: string, examplePath: string): CompareResult;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Validate an env file
|
|
336
|
+
* @param filePath - Path to .env file
|
|
337
|
+
* @param options - Validation options
|
|
338
|
+
* @returns Validation result
|
|
339
|
+
*/
|
|
340
|
+
export function validate(filePath: string, options?: ValidateOptions): ValidateResult;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Validate a value against a type
|
|
344
|
+
* @param value - Value to validate
|
|
345
|
+
* @param type - Type name
|
|
346
|
+
* @returns Validation result
|
|
347
|
+
*/
|
|
348
|
+
export function validateType(value: string, type: ValidationType): TypeValidationResult;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Check an env file against example and validate
|
|
352
|
+
* @param envPath - Path to .env file
|
|
353
|
+
* @param options - Check options
|
|
354
|
+
* @returns Check result
|
|
355
|
+
*/
|
|
356
|
+
export function check(envPath: string, options?: CheckOptions): CheckResult;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Generate a .env file from .env.example
|
|
360
|
+
* @param examplePath - Path to .env.example
|
|
361
|
+
* @param defaults - Default values to fill in
|
|
362
|
+
* @returns Generated .env content
|
|
363
|
+
*/
|
|
364
|
+
export function generate(examplePath: string, defaults?: Record<string, string>): string;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Get list of variable names from file
|
|
368
|
+
* @param filePath - Path to env file
|
|
369
|
+
* @returns Array of variable names
|
|
370
|
+
*/
|
|
371
|
+
export function list(filePath: string): string[];
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Get a specific variable value
|
|
375
|
+
* @param filePath - Path to env file
|
|
376
|
+
* @param key - Variable name
|
|
377
|
+
* @returns Value or undefined
|
|
378
|
+
*/
|
|
379
|
+
export function get(filePath: string, key: string): string | undefined;
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Detect potential secrets in environment variables
|
|
383
|
+
* @param key - Variable name
|
|
384
|
+
* @param value - Variable value
|
|
385
|
+
* @returns Detection result or null if no secret detected
|
|
386
|
+
*/
|
|
387
|
+
export function detectSecret(key: string, value: string): SecretDetectionResult | null;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Check if a value looks like a placeholder
|
|
391
|
+
* @param value - Value to check
|
|
392
|
+
* @returns True if it looks like a placeholder
|
|
393
|
+
*/
|
|
394
|
+
export function isPlaceholder(value: string): boolean;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Find directories that might contain apps/packages in a monorepo
|
|
398
|
+
* @param rootDir - Root directory to scan
|
|
399
|
+
* @returns Array of directory paths
|
|
400
|
+
*/
|
|
401
|
+
export function findMonorepoApps(rootDir: string): string[];
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Scan a monorepo for env file issues
|
|
405
|
+
* @param rootDir - Root directory of monorepo
|
|
406
|
+
* @param options - Scan options
|
|
407
|
+
* @returns Monorepo scan result
|
|
408
|
+
*/
|
|
409
|
+
export function scanMonorepo(rootDir: string, options?: ScanMonorepoOptions): MonorepoScanResult;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Format monorepo result for CLI output
|
|
413
|
+
* @param result - Monorepo scan result
|
|
414
|
+
* @param options - Format options
|
|
415
|
+
* @returns Formatted string
|
|
416
|
+
*/
|
|
417
|
+
export function formatMonorepoResult(result: MonorepoScanResult, options?: FormatMonorepoOptions): string;
|
|
418
|
+
|
|
419
|
+
// ============================================================================
|
|
420
|
+
// Exported Constants
|
|
421
|
+
// ============================================================================
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Map of type names to validator functions
|
|
425
|
+
*/
|
|
426
|
+
export const typeValidators: Record<ValidationType, TypeValidator>;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Array of secret detection patterns
|
|
430
|
+
*/
|
|
431
|
+
export const secretPatterns: SecretPattern[];
|