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,307 @@
1
+ /**
2
+ * Lazy loading system for heavy dependencies and parsers
3
+ */
4
+ export class LazyLoader {
5
+ private static instances = new Map<string, any>();
6
+ private static loadPromises = new Map<string, Promise<any>>();
7
+
8
+ /**
9
+ * Lazy load web-features package
10
+ */
11
+ static async getWebFeatures(): Promise<any> {
12
+ const key = 'web-features';
13
+
14
+ if (this.instances.has(key)) {
15
+ return this.instances.get(key);
16
+ }
17
+
18
+ if (this.loadPromises.has(key)) {
19
+ return this.loadPromises.get(key);
20
+ }
21
+
22
+ const loadPromise = this.loadWebFeatures();
23
+ this.loadPromises.set(key, loadPromise);
24
+
25
+ try {
26
+ const webFeatures = await loadPromise;
27
+ this.instances.set(key, webFeatures);
28
+ this.loadPromises.delete(key);
29
+ return webFeatures;
30
+ } catch (error) {
31
+ this.loadPromises.delete(key);
32
+ throw error;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Lazy load Babel parser
38
+ */
39
+ static async getBabelParser(): Promise<any> {
40
+ const key = 'babel-parser';
41
+
42
+ if (this.instances.has(key)) {
43
+ return this.instances.get(key);
44
+ }
45
+
46
+ if (this.loadPromises.has(key)) {
47
+ return this.loadPromises.get(key);
48
+ }
49
+
50
+ const loadPromise = import('@babel/parser');
51
+ this.loadPromises.set(key, loadPromise);
52
+
53
+ try {
54
+ const parser = await loadPromise;
55
+ this.instances.set(key, parser);
56
+ this.loadPromises.delete(key);
57
+ return parser;
58
+ } catch (error) {
59
+ this.loadPromises.delete(key);
60
+ throw error;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Lazy load Babel traverse
66
+ */
67
+ static async getBabelTraverse(): Promise<any> {
68
+ const key = 'babel-traverse';
69
+
70
+ if (this.instances.has(key)) {
71
+ return this.instances.get(key);
72
+ }
73
+
74
+ if (this.loadPromises.has(key)) {
75
+ return this.loadPromises.get(key);
76
+ }
77
+
78
+ const loadPromise = import('@babel/traverse');
79
+ this.loadPromises.set(key, loadPromise);
80
+
81
+ try {
82
+ const traverse = await loadPromise;
83
+ this.instances.set(key, traverse.default || traverse);
84
+ this.loadPromises.delete(key);
85
+ return this.instances.get(key);
86
+ } catch (error) {
87
+ this.loadPromises.delete(key);
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Lazy load PostCSS
94
+ */
95
+ static async getPostCSS(): Promise<any> {
96
+ const key = 'postcss';
97
+
98
+ if (this.instances.has(key)) {
99
+ return this.instances.get(key);
100
+ }
101
+
102
+ if (this.loadPromises.has(key)) {
103
+ return this.loadPromises.get(key);
104
+ }
105
+
106
+ const loadPromise = import('postcss');
107
+ this.loadPromises.set(key, loadPromise);
108
+
109
+ try {
110
+ const postcss = await loadPromise;
111
+ this.instances.set(key, postcss.default || postcss);
112
+ this.loadPromises.delete(key);
113
+ return this.instances.get(key);
114
+ } catch (error) {
115
+ this.loadPromises.delete(key);
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Lazy load Vue compiler
122
+ */
123
+ static async getVueCompiler(): Promise<any> {
124
+ const key = 'vue-compiler';
125
+
126
+ if (this.instances.has(key)) {
127
+ return this.instances.get(key);
128
+ }
129
+
130
+ if (this.loadPromises.has(key)) {
131
+ return this.loadPromises.get(key);
132
+ }
133
+
134
+ const loadPromise = import('@vue/compiler-sfc');
135
+ this.loadPromises.set(key, loadPromise);
136
+
137
+ try {
138
+ const compiler = await loadPromise;
139
+ this.instances.set(key, compiler);
140
+ this.loadPromises.delete(key);
141
+ return compiler;
142
+ } catch (error) {
143
+ this.loadPromises.delete(key);
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Lazy load Svelte compiler
150
+ */
151
+ static async getSvelteCompiler(): Promise<any> {
152
+ const key = 'svelte-compiler';
153
+
154
+ if (this.instances.has(key)) {
155
+ return this.instances.get(key);
156
+ }
157
+
158
+ if (this.loadPromises.has(key)) {
159
+ return this.loadPromises.get(key);
160
+ }
161
+
162
+ const loadPromise = import('svelte/compiler');
163
+ this.loadPromises.set(key, loadPromise);
164
+
165
+ try {
166
+ const compiler = await loadPromise;
167
+ this.instances.set(key, compiler);
168
+ this.loadPromises.delete(key);
169
+ return compiler;
170
+ } catch (error) {
171
+ this.loadPromises.delete(key);
172
+ throw error;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Preload commonly used dependencies
178
+ */
179
+ static async preloadCommon(): Promise<void> {
180
+ // Preload in background without blocking
181
+ Promise.all([
182
+ this.getWebFeatures().catch(() => {}),
183
+ this.getBabelParser().catch(() => {}),
184
+ this.getPostCSS().catch(() => {})
185
+ ]);
186
+ }
187
+
188
+ /**
189
+ * Clear all cached instances (for testing)
190
+ */
191
+ static clearCache(): void {
192
+ this.instances.clear();
193
+ this.loadPromises.clear();
194
+ }
195
+
196
+ /**
197
+ * Get memory usage statistics
198
+ */
199
+ static getStats(): {
200
+ loadedModules: string[];
201
+ pendingLoads: string[];
202
+ memoryUsage?: NodeJS.MemoryUsage;
203
+ } {
204
+ return {
205
+ loadedModules: Array.from(this.instances.keys()),
206
+ pendingLoads: Array.from(this.loadPromises.keys()),
207
+ memoryUsage: process.memoryUsage ? process.memoryUsage() : undefined
208
+ };
209
+ }
210
+
211
+ /**
212
+ * Load web-features with optimized loading
213
+ */
214
+ private static async loadWebFeatures(): Promise<any> {
215
+ try {
216
+ // Try to load web-features package
217
+ const webFeatures = await import('web-features');
218
+
219
+ // Extract only the data we need to reduce memory usage
220
+ const optimizedData = this.optimizeWebFeaturesData(webFeatures.default || webFeatures);
221
+
222
+ return optimizedData;
223
+ } catch (error) {
224
+ console.warn('Failed to load web-features package:', error);
225
+ // Return minimal fallback data
226
+ return {
227
+ features: {},
228
+ browsers: {},
229
+ groups: {}
230
+ };
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Optimize web-features data to reduce memory usage
236
+ */
237
+ private static optimizeWebFeaturesData(webFeatures: any): any {
238
+ if (!webFeatures) {
239
+ return { features: {}, browsers: {}, groups: {} };
240
+ }
241
+
242
+ // Handle both direct export and features property
243
+ const featuresData = webFeatures.features || webFeatures;
244
+
245
+ if (!featuresData || typeof featuresData !== 'object') {
246
+ return { features: {}, browsers: {}, groups: {} };
247
+ }
248
+
249
+ // Create optimized structure with only essential data
250
+ const optimized = {
251
+ features: {} as any,
252
+ browsers: webFeatures.browsers || {},
253
+ groups: webFeatures.groups || {}
254
+ };
255
+
256
+ // Only keep essential feature data to reduce memory footprint
257
+ for (const [featureId, feature] of Object.entries(featuresData)) {
258
+ const f = feature as any;
259
+
260
+ // Skip if not a valid feature object
261
+ if (!f || typeof f !== 'object') {
262
+ continue;
263
+ }
264
+
265
+ // Only store essential compatibility data
266
+ optimized.features[featureId] = {
267
+ name: f.name,
268
+ status: f.status ? {
269
+ baseline: f.status.baseline,
270
+ support: f.status.support
271
+ } : null,
272
+ // Skip heavy data like descriptions, specs, caniuse data, etc.
273
+ };
274
+ }
275
+
276
+ return optimized;
277
+ }
278
+
279
+ /**
280
+ * Get startup performance statistics
281
+ */
282
+ static getStartupStats(): {
283
+ loadedModules: string[];
284
+ pendingLoads: string[];
285
+ startupTime?: number;
286
+ } {
287
+ return {
288
+ loadedModules: Array.from(this.instances.keys()),
289
+ pendingLoads: Array.from(this.loadPromises.keys()),
290
+ startupTime: process.uptime ? Math.round(process.uptime() * 1000) : undefined
291
+ };
292
+ }
293
+
294
+ /**
295
+ * Optimize startup by preloading critical dependencies
296
+ */
297
+ static async optimizeStartup(): Promise<void> {
298
+ // Start loading critical dependencies in background
299
+ const criticalLoads = [
300
+ this.getWebFeatures().catch(() => {}),
301
+ this.getBabelParser().catch(() => {})
302
+ ];
303
+
304
+ // Don't wait for all to complete, just start the process
305
+ Promise.all(criticalLoads);
306
+ }
307
+ }
File without changes
@@ -0,0 +1,294 @@
1
+ import { createReadStream } from 'fs';
2
+ import { createInterface } from 'readline';
3
+
4
+ // Add global type declaration for gc
5
+ declare global {
6
+ var gc: (() => void) | undefined;
7
+ }
8
+
9
+ /**
10
+ * Memory-efficient file processing and streaming
11
+ */
12
+ export class MemoryManager {
13
+ private static readonly MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
14
+ private static readonly CHUNK_SIZE = 64 * 1024; // 64KB chunks
15
+ private static readonly GC_THRESHOLD = 100 * 1024 * 1024; // 100MB
16
+
17
+ /**
18
+ * Check if file should be processed in streaming mode
19
+ */
20
+ static shouldStream(fileSize: number): boolean {
21
+ return fileSize > this.MAX_FILE_SIZE;
22
+ }
23
+
24
+ /**
25
+ * Read large file in chunks using streaming
26
+ */
27
+ static async readFileStreaming(
28
+ filePath: string,
29
+ processor: (chunk: string, lineNumber: number) => Promise<void>
30
+ ): Promise<void> {
31
+ return new Promise((resolve, reject) => {
32
+ const fileStream = createReadStream(filePath, {
33
+ encoding: 'utf8',
34
+ highWaterMark: this.CHUNK_SIZE
35
+ });
36
+
37
+ const rl = createInterface({
38
+ input: fileStream,
39
+ crlfDelay: Infinity
40
+ });
41
+
42
+ let lineNumber = 0;
43
+ let currentChunk = '';
44
+ let chunkLineCount = 0;
45
+
46
+ rl.on('line', async (line) => {
47
+ lineNumber++;
48
+ currentChunk += line + '\n';
49
+ chunkLineCount++;
50
+
51
+ // Process in chunks to avoid memory buildup
52
+ if (chunkLineCount >= 1000) {
53
+ try {
54
+ await processor(currentChunk, lineNumber - chunkLineCount + 1);
55
+ currentChunk = '';
56
+ chunkLineCount = 0;
57
+
58
+ // Force garbage collection if available
59
+ this.tryGarbageCollect();
60
+ } catch (error) {
61
+ rl.close();
62
+ reject(error);
63
+ return;
64
+ }
65
+ }
66
+ });
67
+
68
+ rl.on('close', async () => {
69
+ try {
70
+ // Process remaining chunk
71
+ if (currentChunk.trim()) {
72
+ await processor(currentChunk, lineNumber - chunkLineCount + 1);
73
+ }
74
+ resolve();
75
+ } catch (error) {
76
+ reject(error);
77
+ }
78
+ });
79
+
80
+ rl.on('error', reject);
81
+ fileStream.on('error', reject);
82
+ });
83
+ }
84
+
85
+ /**
86
+ * Process array in memory-efficient batches
87
+ */
88
+ static async processBatches<T, R>(
89
+ items: T[],
90
+ processor: (batch: T[]) => Promise<R[]>,
91
+ batchSize: number = 100
92
+ ): Promise<R[]> {
93
+ const results: R[] = [];
94
+
95
+ for (let i = 0; i < items.length; i += batchSize) {
96
+ const batch = items.slice(i, i + batchSize);
97
+
98
+ try {
99
+ const batchResults = await processor(batch);
100
+ results.push(...batchResults);
101
+
102
+ // Force garbage collection between batches
103
+ this.tryGarbageCollect();
104
+
105
+ // Small delay to prevent overwhelming the system
106
+ if (i + batchSize < items.length) {
107
+ await this.sleep(1);
108
+ }
109
+ } catch (error) {
110
+ console.warn(`Error processing batch ${i}-${i + batchSize}: ${error}`);
111
+ }
112
+ }
113
+
114
+ return results;
115
+ }
116
+
117
+ /**
118
+ * Monitor memory usage and warn if high
119
+ */
120
+ static checkMemoryUsage(): {
121
+ usage: NodeJS.MemoryUsage;
122
+ warning?: string;
123
+ } {
124
+ const usage = process.memoryUsage();
125
+ let warning: string | undefined;
126
+
127
+ // Check if memory usage is high (over 100MB heap used)
128
+ if (usage.heapUsed > this.GC_THRESHOLD) {
129
+ warning = `High memory usage detected: ${Math.round(usage.heapUsed / 1024 / 1024)}MB heap used`;
130
+ }
131
+
132
+ return { usage, warning };
133
+ }
134
+
135
+ /**
136
+ * Try to trigger garbage collection if available
137
+ */
138
+ static tryGarbageCollect(): void {
139
+ if (global.gc) {
140
+ try {
141
+ global.gc();
142
+ } catch (error) {
143
+ // Ignore GC errors
144
+ }
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Sleep utility for batch processing
150
+ */
151
+ static sleep(ms: number): Promise<void> {
152
+ return new Promise(resolve => setTimeout(resolve, ms));
153
+ }
154
+
155
+ /**
156
+ * Create memory-efficient data structure for violations
157
+ */
158
+ static createViolationTracker(): ViolationTracker {
159
+ return new ViolationTracker();
160
+ }
161
+
162
+ /**
163
+ * Optimize object for memory usage by removing undefined properties
164
+ */
165
+ static optimizeObject<T extends Record<string, any>>(obj: T): T {
166
+ const optimized = {} as T;
167
+
168
+ for (const [key, value] of Object.entries(obj)) {
169
+ if (value !== undefined && value !== null) {
170
+ optimized[key as keyof T] = value;
171
+ }
172
+ }
173
+
174
+ return optimized;
175
+ }
176
+
177
+ /**
178
+ * Get memory usage statistics
179
+ */
180
+ static getMemoryStats(): {
181
+ heapUsed: string;
182
+ heapTotal: string;
183
+ external: string;
184
+ rss: string;
185
+ } {
186
+ const usage = process.memoryUsage();
187
+
188
+ return {
189
+ heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
190
+ heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
191
+ external: `${Math.round(usage.external / 1024 / 1024)}MB`,
192
+ rss: `${Math.round(usage.rss / 1024 / 1024)}MB`
193
+ };
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Memory-efficient violation tracking
199
+ */
200
+ class ViolationTracker {
201
+ private violations = new Map<string, any>();
202
+ private fileIndex = new Map<string, number>();
203
+ private nextFileId = 0;
204
+
205
+ /**
206
+ * Add violation with memory optimization
207
+ */
208
+ addViolation(violation: any): void {
209
+ // Optimize file path storage using indices
210
+ let fileId = this.fileIndex.get(violation.file);
211
+ if (fileId === undefined) {
212
+ fileId = this.nextFileId++;
213
+ this.fileIndex.set(violation.file, fileId);
214
+ }
215
+
216
+ // Create optimized violation object
217
+ const optimized = {
218
+ f: violation.feature,
219
+ fid: violation.featureId,
220
+ fi: fileId, // file index instead of full path
221
+ l: violation.line,
222
+ c: violation.column,
223
+ ctx: violation.context?.substring(0, 100), // Limit context length
224
+ b: violation.browser,
225
+ r: violation.required,
226
+ a: violation.actual,
227
+ bs: violation.baselineStatus,
228
+ rs: violation.reason?.substring(0, 200) // Limit reason length
229
+ };
230
+
231
+ const key = `${fileId}-${violation.line}-${violation.feature}`;
232
+ this.violations.set(key, optimized);
233
+ }
234
+
235
+ /**
236
+ * Get all violations with full data
237
+ */
238
+ getViolations(): any[] {
239
+ const result: any[] = [];
240
+ const fileIdToPath = new Map<number, string>();
241
+
242
+ // Create reverse mapping
243
+ for (const [path, id] of this.fileIndex) {
244
+ fileIdToPath.set(id, path);
245
+ }
246
+
247
+ for (const optimized of this.violations.values()) {
248
+ result.push({
249
+ feature: optimized.f,
250
+ featureId: optimized.fid,
251
+ file: fileIdToPath.get(optimized.fi) || 'unknown',
252
+ line: optimized.l,
253
+ column: optimized.c,
254
+ context: optimized.ctx,
255
+ browser: optimized.b,
256
+ required: optimized.r,
257
+ actual: optimized.a,
258
+ baselineStatus: optimized.bs,
259
+ reason: optimized.rs
260
+ });
261
+ }
262
+
263
+ return result;
264
+ }
265
+
266
+ /**
267
+ * Get memory usage statistics
268
+ */
269
+ getStats(): {
270
+ violationCount: number;
271
+ fileCount: number;
272
+ memoryEstimate: string;
273
+ } {
274
+ const violationCount = this.violations.size;
275
+ const fileCount = this.fileIndex.size;
276
+
277
+ // Rough memory estimate (each violation ~200 bytes)
278
+ const memoryEstimate = `${Math.round(violationCount * 200 / 1024)}KB`;
279
+
280
+ return {
281
+ violationCount,
282
+ fileCount,
283
+ memoryEstimate
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Clear all data
289
+ */
290
+ clear(): void {
291
+ this.violations.clear();
292
+ this.fileIndex.clear();
293
+ this.nextFileId = 0;
294
+ }