@vibe-validate/config 0.10.2 → 0.11.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/README.md +100 -166
- package/dist/define-config.d.ts +1 -12
- package/dist/define-config.d.ts.map +1 -1
- package/dist/define-config.js +1 -35
- package/dist/index.d.ts +17 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -37
- package/dist/loader.d.ts +6 -46
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +30 -133
- package/dist/schema.d.ts +56 -160
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +12 -52
- package/package.json +4 -5
- package/vibe-validate.schema.json +6 -62
- package/dist/presets/index.d.ts +0 -27
- package/dist/presets/index.d.ts.map +0 -1
- package/dist/presets/index.js +0 -36
- package/dist/presets/typescript-library.d.ts +0 -9
- package/dist/presets/typescript-library.d.ts.map +0 -1
- package/dist/presets/typescript-library.js +0 -67
- package/dist/presets/typescript-nodejs.d.ts +0 -9
- package/dist/presets/typescript-nodejs.d.ts.map +0 -1
- package/dist/presets/typescript-nodejs.js +0 -64
- package/dist/presets/typescript-react.d.ts +0 -9
- package/dist/presets/typescript-react.d.ts.map +0 -1
- package/dist/presets/typescript-react.js +0 -73
package/dist/loader.js
CHANGED
|
@@ -1,172 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration Loader
|
|
3
3
|
*
|
|
4
|
-
* Loads and resolves vibe-validate configuration from files
|
|
5
|
-
* including preset resolution and config extension.
|
|
6
|
-
*
|
|
7
|
-
* SECURITY MODEL:
|
|
8
|
-
*
|
|
9
|
-
* This loader executes user-provided configuration files as code (TypeScript/JavaScript)
|
|
10
|
-
* or parses them as data (JSON). This is intentional and necessary for flexibility,
|
|
11
|
-
* but has security implications:
|
|
12
|
-
*
|
|
13
|
-
* **Trust Boundary**: Configuration files are treated as TRUSTED CODE.
|
|
14
|
-
* - Config files can execute arbitrary JavaScript/TypeScript
|
|
15
|
-
* - Config files define shell commands that will be executed during validation
|
|
16
|
-
* - Users MUST only use config files from trusted sources
|
|
17
|
-
*
|
|
18
|
-
* **No Sandboxing**: Configuration files run with full process permissions.
|
|
19
|
-
* - They have access to the file system, network, environment variables
|
|
20
|
-
* - They can import arbitrary npm packages
|
|
21
|
-
* - They can modify process.env or global state
|
|
22
|
-
*
|
|
23
|
-
* **Security Responsibilities**:
|
|
24
|
-
* - **Users**: Only use configs from trusted sources (own code, official presets)
|
|
25
|
-
* - **Preset Authors**: Ensure presets don't execute untrusted commands
|
|
26
|
-
* - **This Package**: Validate config schema, but cannot prevent malicious code execution
|
|
27
|
-
*
|
|
28
|
-
* **Mitigations**:
|
|
29
|
-
* - Configuration schema validation (Zod) ensures structure is correct
|
|
30
|
-
* - Git command injection prevention (array-based spawn, no shell)
|
|
31
|
-
* - No automatic config downloads from remote sources
|
|
32
|
-
* - Presets are vetted and included in this package
|
|
33
|
-
*
|
|
34
|
-
* See SECURITY.md for complete security considerations.
|
|
4
|
+
* Loads and resolves vibe-validate configuration from YAML files.
|
|
35
5
|
*/
|
|
36
6
|
import { resolve, dirname } from 'path';
|
|
37
7
|
import { readFileSync } from 'fs';
|
|
38
|
-
import {
|
|
39
|
-
import { load as parseYaml } from 'js-yaml';
|
|
8
|
+
import { parse as parseYaml } from 'yaml';
|
|
40
9
|
import { validateConfig } from './schema.js';
|
|
41
|
-
import { mergeConfig } from './define-config.js';
|
|
42
|
-
import { getPreset } from './presets/index.js';
|
|
43
10
|
/**
|
|
44
|
-
* Configuration file
|
|
11
|
+
* Configuration file name
|
|
45
12
|
*
|
|
46
|
-
* YAML is
|
|
47
|
-
* .mjs is legacy (deprecated) - supported for migration only.
|
|
13
|
+
* Only YAML format is supported.
|
|
48
14
|
*/
|
|
49
|
-
export const
|
|
50
|
-
'vibe-validate.config.yaml',
|
|
51
|
-
'vibe-validate.config.mjs', // DEPRECATED: Legacy format, will be removed in v1.0
|
|
52
|
-
];
|
|
15
|
+
export const CONFIG_FILE_NAME = 'vibe-validate.config.yaml';
|
|
53
16
|
/**
|
|
54
17
|
* Load configuration from a file path
|
|
55
18
|
*
|
|
56
|
-
* @param configPath - Absolute path to config file
|
|
19
|
+
* @param configPath - Absolute path to config file (must be .yaml)
|
|
57
20
|
* @returns Loaded and validated configuration
|
|
58
21
|
* @throws Error if file cannot be loaded or is invalid
|
|
59
22
|
*/
|
|
60
23
|
export async function loadConfigFromFile(configPath) {
|
|
61
24
|
const absolutePath = resolve(configPath);
|
|
62
|
-
// YAML files
|
|
63
|
-
if (absolutePath.endsWith('.yaml')) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (raw && typeof raw === 'object' && '$schema' in raw) {
|
|
68
|
-
delete raw['$schema'];
|
|
69
|
-
}
|
|
70
|
-
return await resolveConfig(raw, dirname(absolutePath));
|
|
25
|
+
// Only YAML files supported
|
|
26
|
+
if (!absolutePath.endsWith('.yaml')) {
|
|
27
|
+
throw new Error(`Unsupported config file format: ${absolutePath}\n` +
|
|
28
|
+
`Only .yaml format is supported.\n` +
|
|
29
|
+
`Please use vibe-validate.config.yaml`);
|
|
71
30
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const fileUrl = pathToFileURL(absolutePath).href;
|
|
78
|
-
const module = await import(fileUrl);
|
|
79
|
-
const raw = module.default || module;
|
|
80
|
-
return await resolveConfig(raw, dirname(absolutePath));
|
|
31
|
+
const content = readFileSync(absolutePath, 'utf-8');
|
|
32
|
+
const raw = parseYaml(content);
|
|
33
|
+
// Remove $schema property if present (used for IDE support only)
|
|
34
|
+
if (raw && typeof raw === 'object' && '$schema' in raw) {
|
|
35
|
+
delete raw['$schema'];
|
|
81
36
|
}
|
|
82
|
-
|
|
83
|
-
`Only .yaml and .mjs (deprecated) formats are supported.\n` +
|
|
84
|
-
`Please use vibe-validate.config.yaml`);
|
|
37
|
+
return await resolveConfig(raw, dirname(absolutePath));
|
|
85
38
|
}
|
|
86
39
|
/**
|
|
87
40
|
* Find and load configuration from current working directory
|
|
88
41
|
*
|
|
89
|
-
* Searches for config
|
|
42
|
+
* Searches for vibe-validate.config.yaml and loads it if found.
|
|
90
43
|
*
|
|
91
44
|
* @param cwd - Working directory to search (default: process.cwd())
|
|
92
45
|
* @returns Loaded configuration or undefined if no config found
|
|
93
46
|
*/
|
|
94
47
|
export async function findAndLoadConfig(cwd = process.cwd()) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// File doesn't exist or can't be loaded - try next
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
48
|
+
const configPath = resolve(cwd, CONFIG_FILE_NAME);
|
|
49
|
+
try {
|
|
50
|
+
return await loadConfigFromFile(configPath);
|
|
51
|
+
}
|
|
52
|
+
catch (_err) {
|
|
53
|
+
return undefined;
|
|
104
54
|
}
|
|
105
|
-
return undefined;
|
|
106
55
|
}
|
|
107
56
|
/**
|
|
108
|
-
* Resolve
|
|
57
|
+
* Resolve and validate configuration
|
|
109
58
|
*
|
|
110
59
|
* @param raw - Raw configuration object
|
|
111
|
-
* @param
|
|
112
|
-
* @returns
|
|
60
|
+
* @param _basePath - Base directory (unused, kept for compatibility)
|
|
61
|
+
* @returns Validated configuration
|
|
113
62
|
*/
|
|
114
|
-
async function resolveConfig(raw,
|
|
63
|
+
async function resolveConfig(raw, _basePath) {
|
|
115
64
|
if (typeof raw !== 'object' || raw === null) {
|
|
116
65
|
throw new Error('Configuration must be an object');
|
|
117
66
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
let baseConfig;
|
|
121
|
-
if (config.preset) {
|
|
122
|
-
baseConfig = getPreset(config.preset);
|
|
123
|
-
if (!baseConfig) {
|
|
124
|
-
throw new Error(`Unknown preset: ${config.preset}`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// Step 2: Resolve extends if specified
|
|
128
|
-
if (config.extends) {
|
|
129
|
-
// Check if extends is a preset name or a file path
|
|
130
|
-
const extendedConfig = getPreset(config.extends);
|
|
131
|
-
if (extendedConfig) {
|
|
132
|
-
// It's a preset name
|
|
133
|
-
baseConfig = baseConfig
|
|
134
|
-
? mergeConfig(baseConfig, extendedConfig)
|
|
135
|
-
: extendedConfig;
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
// It's a file path - resolve relative to basePath
|
|
139
|
-
const extendsPath = resolve(basePath, config.extends);
|
|
140
|
-
const fileConfig = await loadConfigFromFile(extendsPath);
|
|
141
|
-
baseConfig = baseConfig
|
|
142
|
-
? mergeConfig(baseConfig, fileConfig)
|
|
143
|
-
: fileConfig;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// Step 3: Merge user config with base
|
|
147
|
-
const finalConfig = baseConfig
|
|
148
|
-
? mergeConfig(baseConfig, config)
|
|
149
|
-
: config;
|
|
150
|
-
// Step 4: Validate final configuration
|
|
151
|
-
return validateConfig(finalConfig);
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Load configuration with fallback to default preset
|
|
155
|
-
*
|
|
156
|
-
* Searches for config file, falls back to typescript-library preset if not found.
|
|
157
|
-
*
|
|
158
|
-
* @param cwd - Working directory (default: process.cwd())
|
|
159
|
-
* @returns Configuration (user config or default preset)
|
|
160
|
-
*/
|
|
161
|
-
export async function loadConfigWithFallback(cwd = process.cwd()) {
|
|
162
|
-
const config = await findAndLoadConfig(cwd);
|
|
163
|
-
if (config) {
|
|
164
|
-
return config;
|
|
165
|
-
}
|
|
166
|
-
// Fallback to default preset
|
|
167
|
-
const defaultPreset = getPreset('typescript-library');
|
|
168
|
-
if (!defaultPreset) {
|
|
169
|
-
throw new Error('Default preset not found');
|
|
170
|
-
}
|
|
171
|
-
return defaultPreset;
|
|
67
|
+
// Validate configuration
|
|
68
|
+
return validateConfig(raw);
|
|
172
69
|
}
|