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.
Files changed (244) hide show
  1. package/.eslintrc.json +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +94 -0
  4. package/bin/base.js +494 -0
  5. package/dist/ai/fix-manager.d.ts +67 -0
  6. package/dist/ai/fix-manager.d.ts.map +1 -0
  7. package/dist/ai/fix-manager.js +326 -0
  8. package/dist/ai/fix-manager.js.map +1 -0
  9. package/dist/ai/gemini-analyzer.d.ts +116 -0
  10. package/dist/ai/gemini-analyzer.d.ts.map +1 -0
  11. package/dist/ai/gemini-analyzer.js +572 -0
  12. package/dist/ai/gemini-analyzer.js.map +1 -0
  13. package/dist/ai/index.d.ts +4 -0
  14. package/dist/ai/index.d.ts.map +1 -0
  15. package/dist/ai/index.js +5 -0
  16. package/dist/ai/index.js.map +1 -0
  17. package/dist/ai/jules-implementer.d.ts +115 -0
  18. package/dist/ai/jules-implementer.d.ts.map +1 -0
  19. package/dist/ai/jules-implementer.js +387 -0
  20. package/dist/ai/jules-implementer.js.map +1 -0
  21. package/dist/commands/automation.d.ts +5 -0
  22. package/dist/commands/automation.d.ts.map +1 -0
  23. package/dist/commands/automation.js +305 -0
  24. package/dist/commands/automation.js.map +1 -0
  25. package/dist/commands/check.d.ts +9 -0
  26. package/dist/commands/check.d.ts.map +1 -0
  27. package/dist/commands/check.js +113 -0
  28. package/dist/commands/check.js.map +1 -0
  29. package/dist/commands/config.d.ts +11 -0
  30. package/dist/commands/config.d.ts.map +1 -0
  31. package/dist/commands/config.js +324 -0
  32. package/dist/commands/config.js.map +1 -0
  33. package/dist/commands/fix.d.ts +9 -0
  34. package/dist/commands/fix.d.ts.map +1 -0
  35. package/dist/commands/fix.js +207 -0
  36. package/dist/commands/fix.js.map +1 -0
  37. package/dist/commands/index.d.ts +6 -0
  38. package/dist/commands/index.d.ts.map +1 -0
  39. package/dist/commands/index.js +7 -0
  40. package/dist/commands/index.js.map +1 -0
  41. package/dist/commands/init.d.ts +9 -0
  42. package/dist/commands/init.d.ts.map +1 -0
  43. package/dist/commands/init.js +125 -0
  44. package/dist/commands/init.js.map +1 -0
  45. package/dist/core/api-key-manager.d.ts +83 -0
  46. package/dist/core/api-key-manager.d.ts.map +1 -0
  47. package/dist/core/api-key-manager.js +244 -0
  48. package/dist/core/api-key-manager.js.map +1 -0
  49. package/dist/core/baseguard.d.ts +46 -0
  50. package/dist/core/baseguard.d.ts.map +1 -0
  51. package/dist/core/baseguard.js +132 -0
  52. package/dist/core/baseguard.js.map +1 -0
  53. package/dist/core/baseline-checker.d.ts +63 -0
  54. package/dist/core/baseline-checker.d.ts.map +1 -0
  55. package/dist/core/baseline-checker.js +502 -0
  56. package/dist/core/baseline-checker.js.map +1 -0
  57. package/dist/core/cache-manager.d.ts +88 -0
  58. package/dist/core/cache-manager.d.ts.map +1 -0
  59. package/dist/core/cache-manager.js +213 -0
  60. package/dist/core/cache-manager.js.map +1 -0
  61. package/dist/core/configuration.d.ts +140 -0
  62. package/dist/core/configuration.d.ts.map +1 -0
  63. package/dist/core/configuration.js +474 -0
  64. package/dist/core/configuration.js.map +1 -0
  65. package/dist/core/directory-filter.d.ts +90 -0
  66. package/dist/core/directory-filter.d.ts.map +1 -0
  67. package/dist/core/directory-filter.js +319 -0
  68. package/dist/core/directory-filter.js.map +1 -0
  69. package/dist/core/error-handler.d.ts +110 -0
  70. package/dist/core/error-handler.d.ts.map +1 -0
  71. package/dist/core/error-handler.js +392 -0
  72. package/dist/core/error-handler.js.map +1 -0
  73. package/dist/core/file-processor.d.ts +80 -0
  74. package/dist/core/file-processor.d.ts.map +1 -0
  75. package/dist/core/file-processor.js +259 -0
  76. package/dist/core/file-processor.js.map +1 -0
  77. package/dist/core/gitignore-manager.d.ts +44 -0
  78. package/dist/core/gitignore-manager.d.ts.map +1 -0
  79. package/dist/core/gitignore-manager.js +147 -0
  80. package/dist/core/gitignore-manager.js.map +1 -0
  81. package/dist/core/index.d.ts +13 -0
  82. package/dist/core/index.d.ts.map +1 -0
  83. package/dist/core/index.js +13 -0
  84. package/dist/core/index.js.map +1 -0
  85. package/dist/core/lazy-loader.d.ts +68 -0
  86. package/dist/core/lazy-loader.d.ts.map +1 -0
  87. package/dist/core/lazy-loader.js +260 -0
  88. package/dist/core/lazy-loader.js.map +1 -0
  89. package/dist/core/memory-manager.d.ts +1 -0
  90. package/dist/core/memory-manager.d.ts.map +1 -0
  91. package/dist/core/memory-manager.js +2 -0
  92. package/dist/core/memory-manager.js.map +1 -0
  93. package/dist/core/startup-optimizer.d.ts +45 -0
  94. package/dist/core/startup-optimizer.d.ts.map +1 -0
  95. package/dist/core/startup-optimizer.js +140 -0
  96. package/dist/core/startup-optimizer.js.map +1 -0
  97. package/dist/git/automation-engine.d.ts +58 -0
  98. package/dist/git/automation-engine.d.ts.map +1 -0
  99. package/dist/git/automation-engine.js +318 -0
  100. package/dist/git/automation-engine.js.map +1 -0
  101. package/dist/git/github-manager.d.ts +71 -0
  102. package/dist/git/github-manager.d.ts.map +1 -0
  103. package/dist/git/github-manager.js +226 -0
  104. package/dist/git/github-manager.js.map +1 -0
  105. package/dist/git/hook-manager.d.ts +43 -0
  106. package/dist/git/hook-manager.d.ts.map +1 -0
  107. package/dist/git/hook-manager.js +191 -0
  108. package/dist/git/hook-manager.js.map +1 -0
  109. package/dist/git/index.d.ts +4 -0
  110. package/dist/git/index.d.ts.map +1 -0
  111. package/dist/git/index.js +5 -0
  112. package/dist/git/index.js.map +1 -0
  113. package/dist/index.d.ts +8 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +9 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/parsers/feature-validator.d.ts +60 -0
  118. package/dist/parsers/feature-validator.d.ts.map +1 -0
  119. package/dist/parsers/feature-validator.js +483 -0
  120. package/dist/parsers/feature-validator.js.map +1 -0
  121. package/dist/parsers/index.d.ts +8 -0
  122. package/dist/parsers/index.d.ts.map +1 -0
  123. package/dist/parsers/index.js +9 -0
  124. package/dist/parsers/index.js.map +1 -0
  125. package/dist/parsers/parser-manager.d.ts +103 -0
  126. package/dist/parsers/parser-manager.d.ts.map +1 -0
  127. package/dist/parsers/parser-manager.js +321 -0
  128. package/dist/parsers/parser-manager.js.map +1 -0
  129. package/dist/parsers/parser.d.ts +23 -0
  130. package/dist/parsers/parser.d.ts.map +1 -0
  131. package/dist/parsers/parser.js +6 -0
  132. package/dist/parsers/parser.js.map +1 -0
  133. package/dist/parsers/react-parser.d.ts +22 -0
  134. package/dist/parsers/react-parser.d.ts.map +1 -0
  135. package/dist/parsers/react-parser.js +307 -0
  136. package/dist/parsers/react-parser.js.map +1 -0
  137. package/dist/parsers/svelte-parser.d.ts +33 -0
  138. package/dist/parsers/svelte-parser.d.ts.map +1 -0
  139. package/dist/parsers/svelte-parser.js +408 -0
  140. package/dist/parsers/svelte-parser.js.map +1 -0
  141. package/dist/parsers/vanilla-parser.d.ts +31 -0
  142. package/dist/parsers/vanilla-parser.d.ts.map +1 -0
  143. package/dist/parsers/vanilla-parser.js +590 -0
  144. package/dist/parsers/vanilla-parser.js.map +1 -0
  145. package/dist/parsers/vue-parser.d.ts +9 -0
  146. package/dist/parsers/vue-parser.d.ts.map +1 -0
  147. package/dist/parsers/vue-parser.js +16 -0
  148. package/dist/parsers/vue-parser.js.map +1 -0
  149. package/dist/terminal-header.d.ts +12 -0
  150. package/dist/terminal-header.js +45 -0
  151. package/dist/types/index.d.ts +83 -0
  152. package/dist/types/index.d.ts.map +1 -0
  153. package/dist/types/index.js +5 -0
  154. package/dist/types/index.js.map +1 -0
  155. package/dist/ui/components.d.ts +133 -0
  156. package/dist/ui/components.d.ts.map +1 -0
  157. package/dist/ui/components.js +482 -0
  158. package/dist/ui/components.js.map +1 -0
  159. package/dist/ui/help.d.ts +11 -0
  160. package/dist/ui/help.d.ts.map +1 -0
  161. package/dist/ui/help.js +161 -0
  162. package/dist/ui/help.js.map +1 -0
  163. package/dist/ui/index.d.ts +5 -0
  164. package/dist/ui/index.d.ts.map +1 -0
  165. package/dist/ui/index.js +5 -0
  166. package/dist/ui/index.js.map +1 -0
  167. package/dist/ui/prompts.d.ts +63 -0
  168. package/dist/ui/prompts.d.ts.map +1 -0
  169. package/dist/ui/prompts.js +611 -0
  170. package/dist/ui/prompts.js.map +1 -0
  171. package/dist/ui/terminal-header.d.ts +13 -0
  172. package/dist/ui/terminal-header.d.ts.map +1 -0
  173. package/dist/ui/terminal-header.js +46 -0
  174. package/dist/ui/terminal-header.js.map +1 -0
  175. package/package.json +80 -0
  176. package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
  177. package/src/ai/fix-manager.ts +362 -0
  178. package/src/ai/gemini-analyzer.ts +671 -0
  179. package/src/ai/index.ts +4 -0
  180. package/src/ai/jules-implementer.ts +459 -0
  181. package/src/commands/automation.ts +344 -0
  182. package/src/commands/check.ts +299 -0
  183. package/src/commands/config.ts +365 -0
  184. package/src/commands/fix.ts +234 -0
  185. package/src/commands/index.ts +6 -0
  186. package/src/commands/init.ts +142 -0
  187. package/src/commands/status.ts +0 -0
  188. package/src/core/api-key-manager.ts +298 -0
  189. package/src/core/baseguard.ts +742 -0
  190. package/src/core/baseline-checker.ts +563 -0
  191. package/src/core/cache-manager.ts +270 -0
  192. package/src/core/configuration-recovery.ts +676 -0
  193. package/src/core/configuration.ts +559 -0
  194. package/src/core/debug-logger.ts +590 -0
  195. package/src/core/directory-filter.ts +421 -0
  196. package/src/core/error-handler.ts +517 -0
  197. package/src/core/file-processor.ts +331 -0
  198. package/src/core/gitignore-manager.ts +169 -0
  199. package/src/core/graceful-degradation-manager.ts +596 -0
  200. package/src/core/index.ts +13 -0
  201. package/src/core/lazy-loader.ts +307 -0
  202. package/src/core/logger.ts +0 -0
  203. package/src/core/memory-manager.ts +294 -0
  204. package/src/core/startup-optimizer.ts +173 -0
  205. package/src/core/system-error-handler.ts +746 -0
  206. package/src/git/automation-engine.ts +361 -0
  207. package/src/git/github-manager.ts +260 -0
  208. package/src/git/hook-manager.ts +210 -0
  209. package/src/git/index.ts +4 -0
  210. package/src/index.ts +8 -0
  211. package/src/parsers/feature-validator.ts +559 -0
  212. package/src/parsers/index.ts +8 -0
  213. package/src/parsers/parser-manager.ts +419 -0
  214. package/src/parsers/parser.ts +26 -0
  215. package/src/parsers/react-parser-optimized.ts +161 -0
  216. package/src/parsers/react-parser.ts +359 -0
  217. package/src/parsers/svelte-parser.ts +506 -0
  218. package/src/parsers/vanilla-parser.ts +682 -0
  219. package/src/parsers/vue-parser.ts +472 -0
  220. package/src/types/index.ts +92 -0
  221. package/src/ui/components.ts +567 -0
  222. package/src/ui/help.ts +193 -0
  223. package/src/ui/index.ts +4 -0
  224. package/src/ui/prompts.ts +688 -0
  225. package/src/ui/terminal-header.ts +59 -0
  226. package/test-config-commands.js +56 -0
  227. package/test-header-simple.js +33 -0
  228. package/test-terminal-header.js +12 -0
  229. package/test-ui.js +29 -0
  230. package/tests/e2e/baseguard.e2e.test.ts +516 -0
  231. package/tests/e2e/cross-platform.e2e.test.ts +420 -0
  232. package/tests/e2e/git-integration.e2e.test.ts +487 -0
  233. package/tests/fixtures/react-project/package.json +14 -0
  234. package/tests/fixtures/react-project/src/App.css +76 -0
  235. package/tests/fixtures/react-project/src/App.tsx +77 -0
  236. package/tests/fixtures/svelte-project/package.json +11 -0
  237. package/tests/fixtures/svelte-project/src/App.svelte +369 -0
  238. package/tests/fixtures/vanilla-project/index.html +76 -0
  239. package/tests/fixtures/vanilla-project/script.js +331 -0
  240. package/tests/fixtures/vanilla-project/styles.css +359 -0
  241. package/tests/fixtures/vue-project/package.json +12 -0
  242. package/tests/fixtures/vue-project/src/App.vue +216 -0
  243. package/tsconfig.json +36 -0
  244. 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
+ }