@vibe-validate/config 0.10.3 → 0.12.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/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 { pathToFileURL } from 'url';
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 names to search for (in order)
11
+ * Configuration file name
45
12
  *
46
- * YAML is the primary and recommended format.
47
- * .mjs is legacy (deprecated) - supported for migration only.
13
+ * Only YAML format is supported.
48
14
  */
49
- export const CONFIG_FILE_NAMES = [
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 (primary format)
63
- if (absolutePath.endsWith('.yaml')) {
64
- const content = readFileSync(absolutePath, 'utf-8');
65
- const raw = parseYaml(content);
66
- // Remove $schema property if present (used for IDE support only)
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
- // Legacy .mjs files (DEPRECATED - will be removed in v1.0)
73
- if (absolutePath.endsWith('.mjs')) {
74
- console.warn('⚠️ WARNING: .mjs config format is deprecated and will be removed in v1.0');
75
- console.warn(' Please migrate to vibe-validate.config.yaml');
76
- console.warn(' Run: vibe-validate doctor for migration guidance\n');
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
- throw new Error(`Unsupported config file format: ${absolutePath}\n` +
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 files in order and loads the first one found.
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
- for (const fileName of CONFIG_FILE_NAMES) {
96
- const configPath = resolve(cwd, fileName);
97
- try {
98
- return await loadConfigFromFile(configPath);
99
- }
100
- catch (_err) {
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 configuration with preset and extends support
57
+ * Resolve and validate configuration
109
58
  *
110
59
  * @param raw - Raw configuration object
111
- * @param basePath - Base directory for resolving extends paths
112
- * @returns Resolved and validated configuration
60
+ * @param _basePath - Base directory (unused, kept for compatibility)
61
+ * @returns Validated configuration
113
62
  */
114
- async function resolveConfig(raw, basePath) {
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
- const config = raw;
119
- // Step 1: Resolve preset if specified
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
  }