baseguard 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/.eslintrc.json +25 -0
- package/.prettierrc +8 -0
- package/README.md +94 -0
- package/bin/base.js +494 -0
- package/dist/ai/fix-manager.d.ts +67 -0
- package/dist/ai/fix-manager.d.ts.map +1 -0
- package/dist/ai/fix-manager.js +326 -0
- package/dist/ai/fix-manager.js.map +1 -0
- package/dist/ai/gemini-analyzer.d.ts +116 -0
- package/dist/ai/gemini-analyzer.d.ts.map +1 -0
- package/dist/ai/gemini-analyzer.js +572 -0
- package/dist/ai/gemini-analyzer.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +5 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/jules-implementer.d.ts +115 -0
- package/dist/ai/jules-implementer.d.ts.map +1 -0
- package/dist/ai/jules-implementer.js +387 -0
- package/dist/ai/jules-implementer.js.map +1 -0
- package/dist/commands/automation.d.ts +5 -0
- package/dist/commands/automation.d.ts.map +1 -0
- package/dist/commands/automation.js +305 -0
- package/dist/commands/automation.js.map +1 -0
- package/dist/commands/check.d.ts +9 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +113 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +324 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/fix.d.ts +9 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +207 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/core/api-key-manager.d.ts +83 -0
- package/dist/core/api-key-manager.d.ts.map +1 -0
- package/dist/core/api-key-manager.js +244 -0
- package/dist/core/api-key-manager.js.map +1 -0
- package/dist/core/baseguard.d.ts +46 -0
- package/dist/core/baseguard.d.ts.map +1 -0
- package/dist/core/baseguard.js +132 -0
- package/dist/core/baseguard.js.map +1 -0
- package/dist/core/baseline-checker.d.ts +63 -0
- package/dist/core/baseline-checker.d.ts.map +1 -0
- package/dist/core/baseline-checker.js +502 -0
- package/dist/core/baseline-checker.js.map +1 -0
- package/dist/core/cache-manager.d.ts +88 -0
- package/dist/core/cache-manager.d.ts.map +1 -0
- package/dist/core/cache-manager.js +213 -0
- package/dist/core/cache-manager.js.map +1 -0
- package/dist/core/configuration.d.ts +140 -0
- package/dist/core/configuration.d.ts.map +1 -0
- package/dist/core/configuration.js +474 -0
- package/dist/core/configuration.js.map +1 -0
- package/dist/core/directory-filter.d.ts +90 -0
- package/dist/core/directory-filter.d.ts.map +1 -0
- package/dist/core/directory-filter.js +319 -0
- package/dist/core/directory-filter.js.map +1 -0
- package/dist/core/error-handler.d.ts +110 -0
- package/dist/core/error-handler.d.ts.map +1 -0
- package/dist/core/error-handler.js +392 -0
- package/dist/core/error-handler.js.map +1 -0
- package/dist/core/file-processor.d.ts +80 -0
- package/dist/core/file-processor.d.ts.map +1 -0
- package/dist/core/file-processor.js +259 -0
- package/dist/core/file-processor.js.map +1 -0
- package/dist/core/gitignore-manager.d.ts +44 -0
- package/dist/core/gitignore-manager.d.ts.map +1 -0
- package/dist/core/gitignore-manager.js +147 -0
- package/dist/core/gitignore-manager.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lazy-loader.d.ts +68 -0
- package/dist/core/lazy-loader.d.ts.map +1 -0
- package/dist/core/lazy-loader.js +260 -0
- package/dist/core/lazy-loader.js.map +1 -0
- package/dist/core/memory-manager.d.ts +1 -0
- package/dist/core/memory-manager.d.ts.map +1 -0
- package/dist/core/memory-manager.js +2 -0
- package/dist/core/memory-manager.js.map +1 -0
- package/dist/core/startup-optimizer.d.ts +45 -0
- package/dist/core/startup-optimizer.d.ts.map +1 -0
- package/dist/core/startup-optimizer.js +140 -0
- package/dist/core/startup-optimizer.js.map +1 -0
- package/dist/git/automation-engine.d.ts +58 -0
- package/dist/git/automation-engine.d.ts.map +1 -0
- package/dist/git/automation-engine.js +318 -0
- package/dist/git/automation-engine.js.map +1 -0
- package/dist/git/github-manager.d.ts +71 -0
- package/dist/git/github-manager.d.ts.map +1 -0
- package/dist/git/github-manager.js +226 -0
- package/dist/git/github-manager.js.map +1 -0
- package/dist/git/hook-manager.d.ts +43 -0
- package/dist/git/hook-manager.d.ts.map +1 -0
- package/dist/git/hook-manager.js +191 -0
- package/dist/git/hook-manager.js.map +1 -0
- package/dist/git/index.d.ts +4 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +5 -0
- package/dist/git/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/feature-validator.d.ts +60 -0
- package/dist/parsers/feature-validator.d.ts.map +1 -0
- package/dist/parsers/feature-validator.js +483 -0
- package/dist/parsers/feature-validator.js.map +1 -0
- package/dist/parsers/index.d.ts +8 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +9 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/parser-manager.d.ts +103 -0
- package/dist/parsers/parser-manager.d.ts.map +1 -0
- package/dist/parsers/parser-manager.js +321 -0
- package/dist/parsers/parser-manager.js.map +1 -0
- package/dist/parsers/parser.d.ts +23 -0
- package/dist/parsers/parser.d.ts.map +1 -0
- package/dist/parsers/parser.js +6 -0
- package/dist/parsers/parser.js.map +1 -0
- package/dist/parsers/react-parser.d.ts +22 -0
- package/dist/parsers/react-parser.d.ts.map +1 -0
- package/dist/parsers/react-parser.js +307 -0
- package/dist/parsers/react-parser.js.map +1 -0
- package/dist/parsers/svelte-parser.d.ts +33 -0
- package/dist/parsers/svelte-parser.d.ts.map +1 -0
- package/dist/parsers/svelte-parser.js +408 -0
- package/dist/parsers/svelte-parser.js.map +1 -0
- package/dist/parsers/vanilla-parser.d.ts +31 -0
- package/dist/parsers/vanilla-parser.d.ts.map +1 -0
- package/dist/parsers/vanilla-parser.js +590 -0
- package/dist/parsers/vanilla-parser.js.map +1 -0
- package/dist/parsers/vue-parser.d.ts +9 -0
- package/dist/parsers/vue-parser.d.ts.map +1 -0
- package/dist/parsers/vue-parser.js +16 -0
- package/dist/parsers/vue-parser.js.map +1 -0
- package/dist/terminal-header.d.ts +12 -0
- package/dist/terminal-header.js +45 -0
- package/dist/types/index.d.ts +83 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/components.d.ts +133 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/components.js +482 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/help.d.ts +11 -0
- package/dist/ui/help.d.ts.map +1 -0
- package/dist/ui/help.js +161 -0
- package/dist/ui/help.js.map +1 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/prompts.d.ts +63 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +611 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/terminal-header.d.ts +13 -0
- package/dist/ui/terminal-header.d.ts.map +1 -0
- package/dist/ui/terminal-header.js +46 -0
- package/dist/ui/terminal-header.js.map +1 -0
- package/package.json +80 -0
- package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
- package/src/ai/fix-manager.ts +362 -0
- package/src/ai/gemini-analyzer.ts +671 -0
- package/src/ai/index.ts +4 -0
- package/src/ai/jules-implementer.ts +459 -0
- package/src/commands/automation.ts +344 -0
- package/src/commands/check.ts +299 -0
- package/src/commands/config.ts +365 -0
- package/src/commands/fix.ts +234 -0
- package/src/commands/index.ts +6 -0
- package/src/commands/init.ts +142 -0
- package/src/commands/status.ts +0 -0
- package/src/core/api-key-manager.ts +298 -0
- package/src/core/baseguard.ts +742 -0
- package/src/core/baseline-checker.ts +563 -0
- package/src/core/cache-manager.ts +270 -0
- package/src/core/configuration-recovery.ts +676 -0
- package/src/core/configuration.ts +559 -0
- package/src/core/debug-logger.ts +590 -0
- package/src/core/directory-filter.ts +421 -0
- package/src/core/error-handler.ts +517 -0
- package/src/core/file-processor.ts +331 -0
- package/src/core/gitignore-manager.ts +169 -0
- package/src/core/graceful-degradation-manager.ts +596 -0
- package/src/core/index.ts +13 -0
- package/src/core/lazy-loader.ts +307 -0
- package/src/core/logger.ts +0 -0
- package/src/core/memory-manager.ts +294 -0
- package/src/core/startup-optimizer.ts +173 -0
- package/src/core/system-error-handler.ts +746 -0
- package/src/git/automation-engine.ts +361 -0
- package/src/git/github-manager.ts +260 -0
- package/src/git/hook-manager.ts +210 -0
- package/src/git/index.ts +4 -0
- package/src/index.ts +8 -0
- package/src/parsers/feature-validator.ts +559 -0
- package/src/parsers/index.ts +8 -0
- package/src/parsers/parser-manager.ts +419 -0
- package/src/parsers/parser.ts +26 -0
- package/src/parsers/react-parser-optimized.ts +161 -0
- package/src/parsers/react-parser.ts +359 -0
- package/src/parsers/svelte-parser.ts +506 -0
- package/src/parsers/vanilla-parser.ts +682 -0
- package/src/parsers/vue-parser.ts +472 -0
- package/src/types/index.ts +92 -0
- package/src/ui/components.ts +567 -0
- package/src/ui/help.ts +193 -0
- package/src/ui/index.ts +4 -0
- package/src/ui/prompts.ts +688 -0
- package/src/ui/terminal-header.ts +59 -0
- package/test-config-commands.js +56 -0
- package/test-header-simple.js +33 -0
- package/test-terminal-header.js +12 -0
- package/test-ui.js +29 -0
- package/tests/e2e/baseguard.e2e.test.ts +516 -0
- package/tests/e2e/cross-platform.e2e.test.ts +420 -0
- package/tests/e2e/git-integration.e2e.test.ts +487 -0
- package/tests/fixtures/react-project/package.json +14 -0
- package/tests/fixtures/react-project/src/App.css +76 -0
- package/tests/fixtures/react-project/src/App.tsx +77 -0
- package/tests/fixtures/svelte-project/package.json +11 -0
- package/tests/fixtures/svelte-project/src/App.svelte +369 -0
- package/tests/fixtures/vanilla-project/index.html +76 -0
- package/tests/fixtures/vanilla-project/script.js +331 -0
- package/tests/fixtures/vanilla-project/styles.css +359 -0
- package/tests/fixtures/vue-project/package.json +12 -0
- package/tests/fixtures/vue-project/src/App.vue +216 -0
- package/tsconfig.json +36 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { readFile, writeFile, access } from 'fs/promises';
|
|
2
|
+
import { constants } from 'fs';
|
|
3
|
+
import { GitignoreManager } from './gitignore-manager.js';
|
|
4
|
+
import { UIComponents } from '../ui/components.js';
|
|
5
|
+
import type { Configuration, BrowserTarget } from '../types/index.js';
|
|
6
|
+
|
|
7
|
+
// Preset browser target configurations
|
|
8
|
+
export const BROWSER_TARGET_PRESETS = {
|
|
9
|
+
'baseline-widely': [
|
|
10
|
+
{ browser: 'chrome', minVersion: 'baseline' },
|
|
11
|
+
{ browser: 'firefox', minVersion: 'baseline' },
|
|
12
|
+
{ browser: 'safari', minVersion: 'baseline' },
|
|
13
|
+
{ browser: 'edge', minVersion: 'baseline' }
|
|
14
|
+
] as BrowserTarget[],
|
|
15
|
+
'baseline-newly': [
|
|
16
|
+
{ browser: 'chrome', minVersion: 'baseline-newly' },
|
|
17
|
+
{ browser: 'firefox', minVersion: 'baseline-newly' },
|
|
18
|
+
{ browser: 'safari', minVersion: 'baseline-newly' },
|
|
19
|
+
{ browser: 'edge', minVersion: 'baseline-newly' }
|
|
20
|
+
] as BrowserTarget[],
|
|
21
|
+
'last-2-years': [
|
|
22
|
+
{ browser: 'chrome', minVersion: '100' },
|
|
23
|
+
{ browser: 'firefox', minVersion: '100' },
|
|
24
|
+
{ browser: 'safari', minVersion: '15' },
|
|
25
|
+
{ browser: 'edge', minVersion: '100' }
|
|
26
|
+
] as BrowserTarget[]
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type PresetName = 'baseline-widely' | 'baseline-newly' | 'last-2-years';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Configuration manager for BaseGuard settings
|
|
33
|
+
*/
|
|
34
|
+
export class ConfigurationManager {
|
|
35
|
+
private static readonly CONFIG_FILE = '.baseguardrc.json';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load configuration from file or create default
|
|
39
|
+
*/
|
|
40
|
+
static async load(): Promise<Configuration> {
|
|
41
|
+
try {
|
|
42
|
+
await access(this.CONFIG_FILE, constants.F_OK);
|
|
43
|
+
const content = await readFile(this.CONFIG_FILE, 'utf-8');
|
|
44
|
+
const config = JSON.parse(content) as Configuration;
|
|
45
|
+
|
|
46
|
+
// Validate and migrate configuration if needed
|
|
47
|
+
return this.validateAndMigrate(config);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// File doesn't exist or is invalid, return default
|
|
50
|
+
return this.createDefault();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Save configuration to file
|
|
56
|
+
*/
|
|
57
|
+
static async save(config: Configuration): Promise<void> {
|
|
58
|
+
const validatedConfig = this.validateAndMigrate(config);
|
|
59
|
+
const content = JSON.stringify(validatedConfig, null, 2);
|
|
60
|
+
await writeFile(this.CONFIG_FILE, content, 'utf-8');
|
|
61
|
+
|
|
62
|
+
// Ensure config file is in .gitignore for security
|
|
63
|
+
const gitignoreUpdated = await GitignoreManager.ensureConfigIgnored();
|
|
64
|
+
if (gitignoreUpdated) {
|
|
65
|
+
UIComponents.showInfoBox('Added .baseguardrc.json to .gitignore for security');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create default configuration
|
|
71
|
+
*/
|
|
72
|
+
static createDefault(): Configuration {
|
|
73
|
+
return {
|
|
74
|
+
version: '1.0.0',
|
|
75
|
+
targets: BROWSER_TARGET_PRESETS['baseline-widely'],
|
|
76
|
+
apiKeys: {
|
|
77
|
+
jules: null,
|
|
78
|
+
gemini: null
|
|
79
|
+
},
|
|
80
|
+
automation: {
|
|
81
|
+
enabled: false,
|
|
82
|
+
trigger: 'pre-commit',
|
|
83
|
+
autoAnalyze: true,
|
|
84
|
+
autoFix: false,
|
|
85
|
+
blockCommit: true
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create configuration with preset browser targets
|
|
92
|
+
*/
|
|
93
|
+
static createWithPreset(preset: PresetName): Configuration {
|
|
94
|
+
const config = this.createDefault();
|
|
95
|
+
config.targets = [...BROWSER_TARGET_PRESETS[preset]];
|
|
96
|
+
return config;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Create configuration with custom browser targets
|
|
101
|
+
*/
|
|
102
|
+
static createWithCustomTargets(targets: BrowserTarget[]): Configuration {
|
|
103
|
+
const config = this.createDefault();
|
|
104
|
+
config.targets = this.validateBrowserTargets(targets);
|
|
105
|
+
return config;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Validate and migrate configuration
|
|
110
|
+
*/
|
|
111
|
+
private static validateAndMigrate(config: any): Configuration {
|
|
112
|
+
const defaultConfig = this.createDefault();
|
|
113
|
+
|
|
114
|
+
// Ensure all required fields exist
|
|
115
|
+
const validatedConfig: Configuration = {
|
|
116
|
+
version: config.version || defaultConfig.version,
|
|
117
|
+
targets: this.validateBrowserTargets(config.targets || defaultConfig.targets),
|
|
118
|
+
apiKeys: {
|
|
119
|
+
jules: config.apiKeys?.jules || null,
|
|
120
|
+
gemini: config.apiKeys?.gemini || null
|
|
121
|
+
},
|
|
122
|
+
automation: {
|
|
123
|
+
enabled: config.automation?.enabled ?? defaultConfig.automation.enabled,
|
|
124
|
+
trigger: this.validateTrigger(config.automation?.trigger) || defaultConfig.automation.trigger,
|
|
125
|
+
autoAnalyze: config.automation?.autoAnalyze ?? defaultConfig.automation.autoAnalyze,
|
|
126
|
+
autoFix: config.automation?.autoFix ?? defaultConfig.automation.autoFix,
|
|
127
|
+
blockCommit: config.automation?.blockCommit ?? defaultConfig.automation.blockCommit
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return validatedConfig;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Validate browser targets
|
|
136
|
+
*/
|
|
137
|
+
private static validateBrowserTargets(targets: any[]): BrowserTarget[] {
|
|
138
|
+
if (!Array.isArray(targets) || targets.length === 0) {
|
|
139
|
+
return BROWSER_TARGET_PRESETS['baseline-widely'];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const validTargets: BrowserTarget[] = [];
|
|
143
|
+
const supportedBrowsers = ['chrome', 'firefox', 'safari', 'edge', 'opera', 'samsung'];
|
|
144
|
+
|
|
145
|
+
for (const target of targets) {
|
|
146
|
+
if (typeof target !== 'object' || !target.browser || !target.minVersion) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const browser = target.browser.toLowerCase();
|
|
151
|
+
if (!supportedBrowsers.includes(browser)) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const minVersion = this.validateMinVersion(target.minVersion);
|
|
156
|
+
if (!minVersion) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
validTargets.push({ browser, minVersion });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return validTargets.length > 0 ? validTargets : BROWSER_TARGET_PRESETS['baseline-widely'];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Validate minimum version
|
|
168
|
+
*/
|
|
169
|
+
private static validateMinVersion(minVersion: any): string | null {
|
|
170
|
+
if (typeof minVersion !== 'string') {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Special baseline keywords
|
|
175
|
+
if (minVersion === 'baseline' || minVersion === 'baseline-newly') {
|
|
176
|
+
return minVersion;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Version number validation (basic)
|
|
180
|
+
if (/^\d+(\.\d+)*$/.test(minVersion)) {
|
|
181
|
+
return minVersion;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Validate automation trigger
|
|
189
|
+
*/
|
|
190
|
+
private static validateTrigger(trigger: any): 'pre-commit' | 'pre-push' | null {
|
|
191
|
+
if (trigger === 'pre-commit' || trigger === 'pre-push') {
|
|
192
|
+
return trigger;
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Parse browser target string (e.g., "chrome 100", "safari baseline")
|
|
199
|
+
*/
|
|
200
|
+
static parseBrowserTarget(targetString: string): BrowserTarget | null {
|
|
201
|
+
const parts = targetString.trim().toLowerCase().split(/\s+/);
|
|
202
|
+
|
|
203
|
+
if (parts.length !== 2) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const browser = parts[0];
|
|
208
|
+
const version = parts[1];
|
|
209
|
+
|
|
210
|
+
if (!browser || !version) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const supportedBrowsers = ['chrome', 'firefox', 'safari', 'edge', 'opera', 'samsung'];
|
|
215
|
+
|
|
216
|
+
if (!supportedBrowsers.includes(browser)) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const minVersion = this.validateMinVersion(version);
|
|
221
|
+
if (!minVersion) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { browser, minVersion };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Parse multiple browser targets from strings
|
|
230
|
+
*/
|
|
231
|
+
static parseBrowserTargets(targetStrings: string[]): BrowserTarget[] {
|
|
232
|
+
const targets: BrowserTarget[] = [];
|
|
233
|
+
|
|
234
|
+
for (const targetString of targetStrings) {
|
|
235
|
+
const target = this.parseBrowserTarget(targetString);
|
|
236
|
+
if (target) {
|
|
237
|
+
targets.push(target);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return targets;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get available preset names
|
|
246
|
+
*/
|
|
247
|
+
static getAvailablePresets(): PresetName[] {
|
|
248
|
+
return Object.keys(BROWSER_TARGET_PRESETS) as PresetName[];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get preset description
|
|
253
|
+
*/
|
|
254
|
+
static getPresetDescription(preset: PresetName): string {
|
|
255
|
+
switch (preset) {
|
|
256
|
+
case 'baseline-widely':
|
|
257
|
+
return 'Features supported across all major browsers for 30+ months (Baseline Widely Available)';
|
|
258
|
+
case 'baseline-newly':
|
|
259
|
+
return 'Features newly available across all major browsers (Baseline Newly Available)';
|
|
260
|
+
case 'last-2-years':
|
|
261
|
+
return 'Browser versions from the last 2 years (Chrome 100+, Firefox 100+, Safari 15+, Edge 100+)';
|
|
262
|
+
default:
|
|
263
|
+
return 'Unknown preset';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Check if configuration file exists
|
|
269
|
+
*/
|
|
270
|
+
static async exists(): Promise<boolean> {
|
|
271
|
+
try {
|
|
272
|
+
await access(this.CONFIG_FILE, constants.F_OK);
|
|
273
|
+
return true;
|
|
274
|
+
} catch {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get configuration file path
|
|
281
|
+
*/
|
|
282
|
+
static getConfigFilePath(): string {
|
|
283
|
+
return this.CONFIG_FILE;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Add browser target to existing configuration
|
|
288
|
+
*/
|
|
289
|
+
static async addBrowserTarget(target: BrowserTarget): Promise<void> {
|
|
290
|
+
const config = await this.load();
|
|
291
|
+
|
|
292
|
+
// Remove existing target for the same browser
|
|
293
|
+
config.targets = config.targets.filter(t => t.browser !== target.browser);
|
|
294
|
+
|
|
295
|
+
// Add new target
|
|
296
|
+
config.targets.push(target);
|
|
297
|
+
|
|
298
|
+
await this.save(config);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Remove browser target from existing configuration
|
|
303
|
+
*/
|
|
304
|
+
static async removeBrowserTarget(browser: string): Promise<void> {
|
|
305
|
+
const config = await this.load();
|
|
306
|
+
config.targets = config.targets.filter(t => t.browser !== browser.toLowerCase());
|
|
307
|
+
|
|
308
|
+
// Ensure at least one target remains
|
|
309
|
+
if (config.targets.length === 0) {
|
|
310
|
+
config.targets = BROWSER_TARGET_PRESETS['baseline-widely'];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
await this.save(config);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Update browser targets with preset
|
|
318
|
+
*/
|
|
319
|
+
static async updateWithPreset(preset: PresetName): Promise<void> {
|
|
320
|
+
const config = await this.load();
|
|
321
|
+
config.targets = [...BROWSER_TARGET_PRESETS[preset]];
|
|
322
|
+
await this.save(config);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Update browser targets with custom targets
|
|
327
|
+
*/
|
|
328
|
+
static async updateWithCustomTargets(targets: BrowserTarget[]): Promise<void> {
|
|
329
|
+
const config = await this.load();
|
|
330
|
+
config.targets = this.validateBrowserTargets(targets);
|
|
331
|
+
await this.save(config);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Update automation configuration
|
|
336
|
+
*/
|
|
337
|
+
static async updateAutomation(automationConfig: Partial<Configuration['automation']>): Promise<void> {
|
|
338
|
+
const config = await this.load();
|
|
339
|
+
|
|
340
|
+
config.automation = {
|
|
341
|
+
...config.automation,
|
|
342
|
+
...automationConfig
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// Validate trigger
|
|
346
|
+
if (automationConfig.trigger) {
|
|
347
|
+
const validatedTrigger = this.validateTrigger(automationConfig.trigger);
|
|
348
|
+
if (validatedTrigger) {
|
|
349
|
+
config.automation.trigger = validatedTrigger;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
await this.save(config);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Enable automation
|
|
358
|
+
*/
|
|
359
|
+
static async enableAutomation(trigger?: 'pre-commit' | 'pre-push'): Promise<void> {
|
|
360
|
+
const config = await this.load();
|
|
361
|
+
config.automation.enabled = true;
|
|
362
|
+
|
|
363
|
+
if (trigger) {
|
|
364
|
+
config.automation.trigger = trigger;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
await this.save(config);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Disable automation
|
|
372
|
+
*/
|
|
373
|
+
static async disableAutomation(): Promise<void> {
|
|
374
|
+
const config = await this.load();
|
|
375
|
+
config.automation.enabled = false;
|
|
376
|
+
await this.save(config);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Update API keys
|
|
381
|
+
*/
|
|
382
|
+
static async updateApiKeys(apiKeys: Partial<Configuration['apiKeys']>): Promise<void> {
|
|
383
|
+
const config = await this.load();
|
|
384
|
+
|
|
385
|
+
config.apiKeys = {
|
|
386
|
+
...config.apiKeys,
|
|
387
|
+
...apiKeys
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
await this.save(config);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Validate configuration structure and data
|
|
395
|
+
*/
|
|
396
|
+
static validateConfiguration(config: any): { valid: boolean; errors: string[] } {
|
|
397
|
+
const errors: string[] = [];
|
|
398
|
+
|
|
399
|
+
// Check required fields
|
|
400
|
+
if (!config || typeof config !== 'object') {
|
|
401
|
+
errors.push('Configuration must be an object');
|
|
402
|
+
return { valid: false, errors };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Validate version
|
|
406
|
+
if (!config.version || typeof config.version !== 'string') {
|
|
407
|
+
errors.push('Configuration version is required and must be a string');
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Validate targets
|
|
411
|
+
if (!Array.isArray(config.targets)) {
|
|
412
|
+
errors.push('Browser targets must be an array');
|
|
413
|
+
} else {
|
|
414
|
+
config.targets.forEach((target: any, index: number) => {
|
|
415
|
+
if (!target || typeof target !== 'object') {
|
|
416
|
+
errors.push(`Target ${index} must be an object`);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (!target.browser || typeof target.browser !== 'string') {
|
|
420
|
+
errors.push(`Target ${index} must have a valid browser string`);
|
|
421
|
+
}
|
|
422
|
+
if (!target.minVersion || typeof target.minVersion !== 'string') {
|
|
423
|
+
errors.push(`Target ${index} must have a valid minVersion string`);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Validate API keys
|
|
429
|
+
if (!config.apiKeys || typeof config.apiKeys !== 'object') {
|
|
430
|
+
errors.push('API keys configuration must be an object');
|
|
431
|
+
} else {
|
|
432
|
+
if (config.apiKeys.jules !== null && typeof config.apiKeys.jules !== 'string') {
|
|
433
|
+
errors.push('Jules API key must be a string or null');
|
|
434
|
+
}
|
|
435
|
+
if (config.apiKeys.gemini !== null && typeof config.apiKeys.gemini !== 'string') {
|
|
436
|
+
errors.push('Gemini API key must be a string or null');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Validate automation
|
|
441
|
+
if (!config.automation || typeof config.automation !== 'object') {
|
|
442
|
+
errors.push('Automation configuration must be an object');
|
|
443
|
+
} else {
|
|
444
|
+
const automation = config.automation;
|
|
445
|
+
if (typeof automation.enabled !== 'boolean') {
|
|
446
|
+
errors.push('Automation enabled must be a boolean');
|
|
447
|
+
}
|
|
448
|
+
if (automation.trigger !== 'pre-commit' && automation.trigger !== 'pre-push') {
|
|
449
|
+
errors.push('Automation trigger must be "pre-commit" or "pre-push"');
|
|
450
|
+
}
|
|
451
|
+
if (typeof automation.autoAnalyze !== 'boolean') {
|
|
452
|
+
errors.push('Automation autoAnalyze must be a boolean');
|
|
453
|
+
}
|
|
454
|
+
if (typeof automation.autoFix !== 'boolean') {
|
|
455
|
+
errors.push('Automation autoFix must be a boolean');
|
|
456
|
+
}
|
|
457
|
+
if (typeof automation.blockCommit !== 'boolean') {
|
|
458
|
+
errors.push('Automation blockCommit must be a boolean');
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return { valid: errors.length === 0, errors };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Migrate configuration from older versions
|
|
467
|
+
*/
|
|
468
|
+
static migrateConfiguration(config: any): Configuration {
|
|
469
|
+
// Handle migration from version 0.x to 1.x
|
|
470
|
+
if (!config.version || config.version.startsWith('0.')) {
|
|
471
|
+
// Migrate old structure to new structure
|
|
472
|
+
const migratedConfig = this.createDefault();
|
|
473
|
+
|
|
474
|
+
// Preserve existing settings where possible
|
|
475
|
+
if (config.targets && Array.isArray(config.targets)) {
|
|
476
|
+
migratedConfig.targets = this.validateBrowserTargets(config.targets);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (config.apiKeys) {
|
|
480
|
+
migratedConfig.apiKeys.jules = config.apiKeys.jules || null;
|
|
481
|
+
migratedConfig.apiKeys.gemini = config.apiKeys.gemini || null;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (config.automation) {
|
|
485
|
+
migratedConfig.automation = {
|
|
486
|
+
...migratedConfig.automation,
|
|
487
|
+
...config.automation
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
migratedConfig.version = '1.0.0';
|
|
492
|
+
return migratedConfig;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return config as Configuration;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Get configuration display information
|
|
500
|
+
*/
|
|
501
|
+
static async getConfigurationDisplay(): Promise<{
|
|
502
|
+
config: Configuration;
|
|
503
|
+
security: {
|
|
504
|
+
gitignoreExists: boolean;
|
|
505
|
+
configIgnored: boolean;
|
|
506
|
+
recommendations: string[];
|
|
507
|
+
};
|
|
508
|
+
validation: {
|
|
509
|
+
valid: boolean;
|
|
510
|
+
errors: string[];
|
|
511
|
+
};
|
|
512
|
+
}> {
|
|
513
|
+
const config = await this.load();
|
|
514
|
+
const security = await GitignoreManager.isConfigSecure();
|
|
515
|
+
const validation = this.validateConfiguration(config);
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
config,
|
|
519
|
+
security,
|
|
520
|
+
validation
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Backup current configuration
|
|
526
|
+
*/
|
|
527
|
+
static async backupConfiguration(): Promise<string> {
|
|
528
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
529
|
+
const backupFile = `.baseguardrc.backup.${timestamp}.json`;
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
const config = await this.load();
|
|
533
|
+
const content = JSON.stringify(config, null, 2);
|
|
534
|
+
await writeFile(backupFile, content, 'utf-8');
|
|
535
|
+
return backupFile;
|
|
536
|
+
} catch (error) {
|
|
537
|
+
throw new Error(`Failed to create backup: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Restore configuration from backup
|
|
543
|
+
*/
|
|
544
|
+
static async restoreConfiguration(backupFile: string): Promise<void> {
|
|
545
|
+
try {
|
|
546
|
+
const content = await readFile(backupFile, 'utf-8');
|
|
547
|
+
const config = JSON.parse(content);
|
|
548
|
+
|
|
549
|
+
const validation = this.validateConfiguration(config);
|
|
550
|
+
if (!validation.valid) {
|
|
551
|
+
throw new Error(`Invalid backup configuration: ${validation.errors.join(', ')}`);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
await this.save(config);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
throw new Error(`Failed to restore backup: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|