qa360 1.4.5 → 2.0.1

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 (209) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/ai.d.ts +41 -0
  3. package/dist/commands/ai.js +499 -0
  4. package/dist/commands/ask.js +12 -12
  5. package/dist/commands/coverage.d.ts +8 -0
  6. package/dist/commands/coverage.js +252 -0
  7. package/dist/commands/explain.d.ts +27 -0
  8. package/dist/commands/explain.js +630 -0
  9. package/dist/commands/flakiness.d.ts +73 -0
  10. package/dist/commands/flakiness.js +435 -0
  11. package/dist/commands/generate.d.ts +66 -0
  12. package/dist/commands/generate.js +438 -0
  13. package/dist/commands/init.d.ts +56 -9
  14. package/dist/commands/init.js +217 -10
  15. package/dist/commands/monitor.d.ts +27 -0
  16. package/dist/commands/monitor.js +225 -0
  17. package/dist/commands/ollama.d.ts +40 -0
  18. package/dist/commands/ollama.js +301 -0
  19. package/dist/commands/pack.d.ts +37 -9
  20. package/dist/commands/pack.js +240 -141
  21. package/dist/commands/regression.d.ts +8 -0
  22. package/dist/commands/regression.js +340 -0
  23. package/dist/commands/repair.d.ts +26 -0
  24. package/dist/commands/repair.js +307 -0
  25. package/dist/commands/retry.d.ts +43 -0
  26. package/dist/commands/retry.js +275 -0
  27. package/dist/commands/run.d.ts +8 -3
  28. package/dist/commands/run.js +45 -31
  29. package/dist/commands/slo.d.ts +8 -0
  30. package/dist/commands/slo.js +327 -0
  31. package/dist/core/adapters/playwright-native-api.d.ts +183 -0
  32. package/dist/core/adapters/playwright-native-api.js +461 -0
  33. package/dist/core/adapters/playwright-ui.d.ts +7 -0
  34. package/dist/core/adapters/playwright-ui.js +29 -1
  35. package/dist/core/ai/anthropic-provider.d.ts +50 -0
  36. package/dist/core/ai/anthropic-provider.js +211 -0
  37. package/dist/core/ai/deepseek-provider.d.ts +81 -0
  38. package/dist/core/ai/deepseek-provider.js +254 -0
  39. package/dist/core/ai/index.d.ts +60 -0
  40. package/dist/core/ai/index.js +18 -0
  41. package/dist/core/ai/llm-client.d.ts +45 -0
  42. package/dist/core/ai/llm-client.js +7 -0
  43. package/dist/core/ai/mock-provider.d.ts +49 -0
  44. package/dist/core/ai/mock-provider.js +121 -0
  45. package/dist/core/ai/ollama-provider.d.ts +78 -0
  46. package/dist/core/ai/ollama-provider.js +192 -0
  47. package/dist/core/ai/openai-provider.d.ts +48 -0
  48. package/dist/core/ai/openai-provider.js +188 -0
  49. package/dist/core/ai/provider-factory.d.ts +160 -0
  50. package/dist/core/ai/provider-factory.js +269 -0
  51. package/dist/core/auth/api-key-provider.d.ts +16 -0
  52. package/dist/core/auth/api-key-provider.js +63 -0
  53. package/dist/core/auth/aws-iam-provider.d.ts +35 -0
  54. package/dist/core/auth/aws-iam-provider.js +177 -0
  55. package/dist/core/auth/azure-ad-provider.d.ts +15 -0
  56. package/dist/core/auth/azure-ad-provider.js +99 -0
  57. package/dist/core/auth/basic-auth-provider.d.ts +26 -0
  58. package/dist/core/auth/basic-auth-provider.js +111 -0
  59. package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
  60. package/dist/core/auth/gcp-adc-provider.js +126 -0
  61. package/dist/core/auth/index.d.ts +238 -0
  62. package/dist/core/auth/index.js +82 -0
  63. package/dist/core/auth/jwt-provider.d.ts +19 -0
  64. package/dist/core/auth/jwt-provider.js +160 -0
  65. package/dist/core/auth/manager.d.ts +84 -0
  66. package/dist/core/auth/manager.js +230 -0
  67. package/dist/core/auth/oauth2-provider.d.ts +17 -0
  68. package/dist/core/auth/oauth2-provider.js +114 -0
  69. package/dist/core/auth/totp-provider.d.ts +31 -0
  70. package/dist/core/auth/totp-provider.js +134 -0
  71. package/dist/core/auth/ui-login-provider.d.ts +26 -0
  72. package/dist/core/auth/ui-login-provider.js +198 -0
  73. package/dist/core/cache/index.d.ts +7 -0
  74. package/dist/core/cache/index.js +6 -0
  75. package/dist/core/cache/lru-cache.d.ts +203 -0
  76. package/dist/core/cache/lru-cache.js +397 -0
  77. package/dist/core/coverage/analyzer.d.ts +101 -0
  78. package/dist/core/coverage/analyzer.js +415 -0
  79. package/dist/core/coverage/collector.d.ts +74 -0
  80. package/dist/core/coverage/collector.js +459 -0
  81. package/dist/core/coverage/config.d.ts +37 -0
  82. package/dist/core/coverage/config.js +156 -0
  83. package/dist/core/coverage/index.d.ts +11 -0
  84. package/dist/core/coverage/index.js +15 -0
  85. package/dist/core/coverage/types.d.ts +267 -0
  86. package/dist/core/coverage/types.js +6 -0
  87. package/dist/core/coverage/vault.d.ts +95 -0
  88. package/dist/core/coverage/vault.js +405 -0
  89. package/dist/core/dashboard/assets.d.ts +6 -0
  90. package/dist/core/dashboard/assets.js +690 -0
  91. package/dist/core/dashboard/index.d.ts +6 -0
  92. package/dist/core/dashboard/index.js +5 -0
  93. package/dist/core/dashboard/server.d.ts +72 -0
  94. package/dist/core/dashboard/server.js +354 -0
  95. package/dist/core/dashboard/types.d.ts +70 -0
  96. package/dist/core/dashboard/types.js +5 -0
  97. package/dist/core/discoverer/index.d.ts +115 -0
  98. package/dist/core/discoverer/index.js +250 -0
  99. package/dist/core/flakiness/index.d.ts +228 -0
  100. package/dist/core/flakiness/index.js +384 -0
  101. package/dist/core/generation/code-formatter.d.ts +111 -0
  102. package/dist/core/generation/code-formatter.js +307 -0
  103. package/dist/core/generation/code-generator.d.ts +144 -0
  104. package/dist/core/generation/code-generator.js +293 -0
  105. package/dist/core/generation/generator.d.ts +40 -0
  106. package/dist/core/generation/generator.js +76 -0
  107. package/dist/core/generation/index.d.ts +30 -0
  108. package/dist/core/generation/index.js +28 -0
  109. package/dist/core/generation/pack-generator.d.ts +107 -0
  110. package/dist/core/generation/pack-generator.js +416 -0
  111. package/dist/core/generation/prompt-builder.d.ts +132 -0
  112. package/dist/core/generation/prompt-builder.js +672 -0
  113. package/dist/core/generation/source-analyzer.d.ts +213 -0
  114. package/dist/core/generation/source-analyzer.js +657 -0
  115. package/dist/core/generation/test-optimizer.d.ts +117 -0
  116. package/dist/core/generation/test-optimizer.js +328 -0
  117. package/dist/core/generation/types.d.ts +214 -0
  118. package/dist/core/generation/types.js +4 -0
  119. package/dist/core/index.d.ts +23 -1
  120. package/dist/core/index.js +39 -0
  121. package/dist/core/pack/validator.js +31 -1
  122. package/dist/core/pack-v2/index.d.ts +9 -0
  123. package/dist/core/pack-v2/index.js +8 -0
  124. package/dist/core/pack-v2/loader.d.ts +62 -0
  125. package/dist/core/pack-v2/loader.js +231 -0
  126. package/dist/core/pack-v2/migrator.d.ts +56 -0
  127. package/dist/core/pack-v2/migrator.js +455 -0
  128. package/dist/core/pack-v2/validator.d.ts +61 -0
  129. package/dist/core/pack-v2/validator.js +577 -0
  130. package/dist/core/regression/detector.d.ts +107 -0
  131. package/dist/core/regression/detector.js +497 -0
  132. package/dist/core/regression/index.d.ts +9 -0
  133. package/dist/core/regression/index.js +11 -0
  134. package/dist/core/regression/trend-analyzer.d.ts +102 -0
  135. package/dist/core/regression/trend-analyzer.js +345 -0
  136. package/dist/core/regression/types.d.ts +222 -0
  137. package/dist/core/regression/types.js +7 -0
  138. package/dist/core/regression/vault.d.ts +87 -0
  139. package/dist/core/regression/vault.js +289 -0
  140. package/dist/core/repair/engine/fixer.d.ts +24 -0
  141. package/dist/core/repair/engine/fixer.js +226 -0
  142. package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
  143. package/dist/core/repair/engine/suggestion-engine.js +187 -0
  144. package/dist/core/repair/index.d.ts +10 -0
  145. package/dist/core/repair/index.js +13 -0
  146. package/dist/core/repair/repairer.d.ts +90 -0
  147. package/dist/core/repair/repairer.js +284 -0
  148. package/dist/core/repair/types.d.ts +91 -0
  149. package/dist/core/repair/types.js +6 -0
  150. package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
  151. package/dist/core/repair/utils/error-analyzer.js +264 -0
  152. package/dist/core/retry/flakiness-integration.d.ts +60 -0
  153. package/dist/core/retry/flakiness-integration.js +228 -0
  154. package/dist/core/retry/index.d.ts +14 -0
  155. package/dist/core/retry/index.js +16 -0
  156. package/dist/core/retry/retry-engine.d.ts +80 -0
  157. package/dist/core/retry/retry-engine.js +296 -0
  158. package/dist/core/retry/types.d.ts +178 -0
  159. package/dist/core/retry/types.js +52 -0
  160. package/dist/core/retry/vault.d.ts +77 -0
  161. package/dist/core/retry/vault.js +304 -0
  162. package/dist/core/runner/e2e-helpers.d.ts +102 -0
  163. package/dist/core/runner/e2e-helpers.js +153 -0
  164. package/dist/core/runner/phase3-runner.d.ts +101 -2
  165. package/dist/core/runner/phase3-runner.js +559 -24
  166. package/dist/core/self-healing/assertion-healer.d.ts +97 -0
  167. package/dist/core/self-healing/assertion-healer.js +371 -0
  168. package/dist/core/self-healing/engine.d.ts +122 -0
  169. package/dist/core/self-healing/engine.js +538 -0
  170. package/dist/core/self-healing/index.d.ts +10 -0
  171. package/dist/core/self-healing/index.js +11 -0
  172. package/dist/core/self-healing/selector-healer.d.ts +103 -0
  173. package/dist/core/self-healing/selector-healer.js +372 -0
  174. package/dist/core/self-healing/types.d.ts +152 -0
  175. package/dist/core/self-healing/types.js +6 -0
  176. package/dist/core/slo/config.d.ts +107 -0
  177. package/dist/core/slo/config.js +360 -0
  178. package/dist/core/slo/index.d.ts +11 -0
  179. package/dist/core/slo/index.js +15 -0
  180. package/dist/core/slo/sli-calculator.d.ts +92 -0
  181. package/dist/core/slo/sli-calculator.js +364 -0
  182. package/dist/core/slo/slo-tracker.d.ts +148 -0
  183. package/dist/core/slo/slo-tracker.js +379 -0
  184. package/dist/core/slo/types.d.ts +281 -0
  185. package/dist/core/slo/types.js +7 -0
  186. package/dist/core/slo/vault.d.ts +102 -0
  187. package/dist/core/slo/vault.js +427 -0
  188. package/dist/core/tui/index.d.ts +7 -0
  189. package/dist/core/tui/index.js +6 -0
  190. package/dist/core/tui/monitor.d.ts +92 -0
  191. package/dist/core/tui/monitor.js +271 -0
  192. package/dist/core/tui/renderer.d.ts +33 -0
  193. package/dist/core/tui/renderer.js +218 -0
  194. package/dist/core/tui/types.d.ts +63 -0
  195. package/dist/core/tui/types.js +5 -0
  196. package/dist/core/types/pack-v2.d.ts +425 -0
  197. package/dist/core/types/pack-v2.js +8 -0
  198. package/dist/core/vault/index.d.ts +116 -0
  199. package/dist/core/vault/index.js +400 -5
  200. package/dist/core/watch/index.d.ts +7 -0
  201. package/dist/core/watch/index.js +6 -0
  202. package/dist/core/watch/watch-mode.d.ts +213 -0
  203. package/dist/core/watch/watch-mode.js +389 -0
  204. package/dist/index.js +68 -68
  205. package/dist/utils/config.d.ts +5 -0
  206. package/dist/utils/config.js +136 -0
  207. package/package.json +5 -1
  208. package/dist/core/adapters/playwright-api.d.ts +0 -82
  209. package/dist/core/adapters/playwright-api.js +0 -264
@@ -0,0 +1,497 @@
1
+ /**
2
+ * Regression Detector
3
+ *
4
+ * Detects regressions using statistical methods.
5
+ * Supports Z-score, T-test, Mann-Whitney U, and percentile-based detection.
6
+ */
7
+ /**
8
+ * Regression Detector class
9
+ */
10
+ export class RegressionDetector {
11
+ config;
12
+ baselines = new Map();
13
+ constructor(config) {
14
+ this.config = {
15
+ ...this.createDefaultConfig(),
16
+ ...config
17
+ };
18
+ }
19
+ /**
20
+ * Detect regressions by comparing current values to baseline
21
+ */
22
+ detectRegressions(currentData, currentRunId, gate, context) {
23
+ const regressions = [];
24
+ for (const [metricName, currentValue] of Object.entries(currentData)) {
25
+ if (this.shouldIgnore(metricName)) {
26
+ continue;
27
+ }
28
+ const baseline = this.baselines.get(metricName);
29
+ if (!baseline || baseline.sampleSize < this.config.minDataPoints) {
30
+ continue;
31
+ }
32
+ const regression = this.detectMetricRegression(metricName, currentValue, baseline, currentRunId, gate, context);
33
+ if (regression) {
34
+ regressions.push(regression);
35
+ }
36
+ }
37
+ return regressions;
38
+ }
39
+ /**
40
+ * Detect regression for a single metric
41
+ */
42
+ detectMetricRegression(metricName, currentValue, baseline, runId, gate, context) {
43
+ const threshold = this.getThresholdForMetric(metricName);
44
+ if (!threshold) {
45
+ return null;
46
+ }
47
+ // Calculate statistical significance
48
+ const test = this.performStatisticalTest(currentValue, baseline);
49
+ // Calculate change
50
+ const absoluteChange = currentValue - baseline.mean;
51
+ const percentChange = baseline.mean !== 0
52
+ ? (absoluteChange / baseline.mean) * 100
53
+ : 0;
54
+ // Determine direction
55
+ const direction = this.getDIRECTION(metricName, absoluteChange, threshold);
56
+ // Check if regression detected
57
+ const isRegression = this.isRegression(currentValue, baseline, threshold, test, direction);
58
+ if (!isRegression) {
59
+ return null;
60
+ }
61
+ // Determine severity
62
+ const severity = this.calculateSeverity(absoluteChange, percentChange, threshold, test.pValue);
63
+ // Generate suggestions
64
+ const suggestions = this.generateSuggestions(metricName, direction, severity, currentValue, baseline.mean);
65
+ return {
66
+ id: `regression_${Date.now()}_${metricName.replace(/\W/g, '_')}`,
67
+ runId,
68
+ baselineRunId: baseline.runIds[baseline.runIds.length - 1],
69
+ type: this.getTypeForMetric(metricName),
70
+ severity,
71
+ status: 'detected',
72
+ metricName,
73
+ baselineValue: baseline.mean,
74
+ currentValue,
75
+ absoluteChange,
76
+ percentChange,
77
+ significance: test.pValue,
78
+ confidence: 1 - test.pValue,
79
+ direction,
80
+ affectedComponent: this.extractComponent(metricName, context),
81
+ gate,
82
+ context,
83
+ suggestions,
84
+ timestamp: Date.now()
85
+ };
86
+ }
87
+ /**
88
+ * Perform statistical test
89
+ */
90
+ performStatisticalTest(currentValue, baseline) {
91
+ let test;
92
+ switch (this.config.method) {
93
+ case 'zscore':
94
+ test = this.zScoreTest(currentValue, baseline);
95
+ break;
96
+ case 'ttest':
97
+ test = this.tTest(currentValue, baseline);
98
+ break;
99
+ case 'percentile':
100
+ test = this.percentileTest(currentValue, baseline);
101
+ break;
102
+ case 'mannwhitney':
103
+ default:
104
+ test = this.zScoreTest(currentValue, baseline); // Default to z-score for single value
105
+ }
106
+ return test;
107
+ }
108
+ /**
109
+ * Z-score test
110
+ */
111
+ zScoreTest(value, baseline) {
112
+ if (baseline.stdDev === 0) {
113
+ // When baseline has no variance, any change is highly significant
114
+ if (value !== baseline.mean) {
115
+ return {
116
+ test: 'zscore',
117
+ pValue: 0.0001, // Very small p-value for high significance
118
+ significant: true,
119
+ statistic: value > baseline.mean ? Infinity : -Infinity
120
+ };
121
+ }
122
+ return {
123
+ test: 'zscore',
124
+ pValue: 1,
125
+ significant: false,
126
+ statistic: 0
127
+ };
128
+ }
129
+ const zScore = (value - baseline.mean) / baseline.stdDev;
130
+ // Two-tailed p-value
131
+ const pValue = 2 * (1 - this.normalCDF(Math.abs(zScore)));
132
+ return {
133
+ test: 'zscore',
134
+ pValue,
135
+ significant: pValue < 0.05,
136
+ statistic: zScore,
137
+ confidenceInterval: {
138
+ lower: baseline.mean - 1.96 * baseline.stdDev,
139
+ upper: baseline.mean + 1.96 * baseline.stdDev
140
+ }
141
+ };
142
+ }
143
+ /**
144
+ * T-test (simplified for single value vs baseline)
145
+ */
146
+ tTest(value, baseline) {
147
+ // For single value, use z-score approximation with n-1 degrees of freedom
148
+ const df = baseline.sampleSize - 1;
149
+ const tScore = (value - baseline.mean) / (baseline.stdDev / Math.sqrt(baseline.sampleSize));
150
+ // Approximate p-value (simplified)
151
+ const pValue = 2 * (1 - this.studentTCDF(Math.abs(tScore), df));
152
+ return {
153
+ test: 'ttest',
154
+ pValue,
155
+ significant: pValue < 0.05,
156
+ statistic: tScore
157
+ };
158
+ }
159
+ /**
160
+ * Percentile test
161
+ */
162
+ percentileTest(value, baseline) {
163
+ const percentile = this.calculatePercentile(value, baseline);
164
+ // P-value based on percentile (extreme values have low p-value)
165
+ const pValue = percentile > 95
166
+ ? (100 - percentile) / 100
167
+ : percentile < 5
168
+ ? percentile / 100
169
+ : 0.5;
170
+ return {
171
+ test: 'percentile',
172
+ pValue,
173
+ significant: pValue < 0.05,
174
+ statistic: percentile
175
+ };
176
+ }
177
+ /**
178
+ * Calculate percentile for a value in baseline
179
+ */
180
+ calculatePercentile(value, baseline) {
181
+ if (value < baseline.min)
182
+ return 0;
183
+ if (value > baseline.max)
184
+ return 100;
185
+ // Estimate percentile using mean and stdDev (assuming normal distribution)
186
+ const zScore = (value - baseline.mean) / baseline.stdDev;
187
+ return this.normalCDF(zScore) * 100;
188
+ }
189
+ /**
190
+ * Normal CDF (cumulative distribution function)
191
+ */
192
+ normalCDF(x) {
193
+ // Approximation of normal CDF
194
+ const a1 = 0.254829592;
195
+ const a2 = -0.284496736;
196
+ const a3 = 1.421413741;
197
+ const a4 = -1.453152027;
198
+ const a5 = 1.061405429;
199
+ const p = 0.3275911;
200
+ const sign = x < 0 ? -1 : 1;
201
+ x = Math.abs(x) / Math.sqrt(2);
202
+ const t = 1.0 / (1.0 + p * x);
203
+ const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
204
+ return 0.5 * (1.0 + sign * y);
205
+ }
206
+ /**
207
+ * Student's t CDF approximation
208
+ */
209
+ studentTCDF(t, df) {
210
+ // Simplified approximation for large df
211
+ if (df > 30) {
212
+ return this.normalCDF(t);
213
+ }
214
+ // For small df, use approximation
215
+ return this.normalCDF(t * 0.9); // Conservative approximation
216
+ }
217
+ /**
218
+ * Check if change constitutes a regression
219
+ */
220
+ isRegression(currentValue, baseline, threshold, test, direction) {
221
+ // Must be statistically significant
222
+ if (!test.significant && test.pValue > threshold.significanceLevel) {
223
+ return false;
224
+ }
225
+ // Must exceed minimum delta
226
+ const absChange = Math.abs(currentValue - baseline.mean);
227
+ if (absChange < threshold.minDelta) {
228
+ return false;
229
+ }
230
+ // Must be in the "worse" direction
231
+ if (direction !== 'worse') {
232
+ return false;
233
+ }
234
+ // For percentage-based thresholds
235
+ const percentChange = baseline.mean !== 0
236
+ ? (absChange / baseline.mean) * 100
237
+ : 0;
238
+ return percentChange > threshold.maxRegression;
239
+ }
240
+ /**
241
+ * Get direction of change (worse/better/neutral)
242
+ */
243
+ getDIRECTION(metricName, change, threshold) {
244
+ // For 'increase' direction metrics (like latency): higher value is worse
245
+ // For 'decrease' direction metrics (like coverage): lower value is worse
246
+ const isWorse = (threshold.direction === 'increase' && change > 0) ||
247
+ (threshold.direction === 'decrease' && change < 0);
248
+ const isBetter = (threshold.direction === 'increase' && change < 0) ||
249
+ (threshold.direction === 'decrease' && change > 0);
250
+ if (isWorse) {
251
+ return 'worse';
252
+ }
253
+ if (isBetter) {
254
+ return 'better';
255
+ }
256
+ return 'neutral';
257
+ }
258
+ /**
259
+ * Calculate regression severity
260
+ */
261
+ calculateSeverity(absoluteChange, percentChange, threshold, pValue) {
262
+ // Critical: extreme regression or very high significance
263
+ if (percentChange > threshold.maxRegression * 5 || pValue < 0.001) {
264
+ return 'critical';
265
+ }
266
+ // Major: severe regression
267
+ if (percentChange > threshold.maxRegression * 3 || pValue < 0.01) {
268
+ return 'major';
269
+ }
270
+ // Moderate: notable regression
271
+ if (percentChange > threshold.maxRegression * 2 || pValue < 0.05) {
272
+ return 'moderate';
273
+ }
274
+ // Minor: slight regression
275
+ return 'minor';
276
+ }
277
+ /**
278
+ * Generate suggestions for regression
279
+ */
280
+ generateSuggestions(metricName, direction, severity, currentValue, baselineValue) {
281
+ const suggestions = [];
282
+ if (direction === 'worse') {
283
+ suggestions.push(`Investigate recent changes to ${metricName}`);
284
+ suggestions.push(`Compare with baseline: ${baselineValue.toFixed(2)} → ${currentValue.toFixed(2)}`);
285
+ if (severity === 'critical' || severity === 'major') {
286
+ suggestions.push('Consider rolling back recent changes');
287
+ suggestions.push('Review test environment for external factors');
288
+ }
289
+ if (metricName.includes('duration') || metricName.includes('latency')) {
290
+ suggestions.push('Check for infinite loops or blocking operations');
291
+ suggestions.push('Profile the code to identify bottlenecks');
292
+ }
293
+ if (metricName.includes('memory') || metricName.includes('heap')) {
294
+ suggestions.push('Check for memory leaks');
295
+ suggestions.push('Review allocation patterns');
296
+ }
297
+ }
298
+ return suggestions;
299
+ }
300
+ /**
301
+ * Update baseline with new data
302
+ */
303
+ updateBaseline(metricName, data) {
304
+ if (data.length === 0)
305
+ return;
306
+ const values = data.map(d => d.value);
307
+ const sorted = [...values].sort((a, b) => a - b);
308
+ const mean = values.reduce((sum, v) => sum + v, 0) / values.length;
309
+ const variance = values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length;
310
+ const stdDev = Math.sqrt(variance);
311
+ this.baselines.set(metricName, {
312
+ metric: metricName,
313
+ mean,
314
+ stdDev,
315
+ min: sorted[0],
316
+ max: sorted[sorted.length - 1],
317
+ percentiles: {
318
+ p50: sorted[Math.floor(sorted.length * 0.5)],
319
+ p90: sorted[Math.floor(sorted.length * 0.9)],
320
+ p95: sorted[Math.floor(sorted.length * 0.95)],
321
+ p99: sorted[Math.floor(sorted.length * 0.99)]
322
+ },
323
+ sampleSize: values.length,
324
+ runIds: data.map(d => d.runId),
325
+ timestamp: Date.now()
326
+ });
327
+ }
328
+ /**
329
+ * Get baseline for a metric
330
+ */
331
+ getBaseline(metricName) {
332
+ return this.baselines.get(metricName);
333
+ }
334
+ /**
335
+ * Get all baselines
336
+ */
337
+ getAllBaselines() {
338
+ return Array.from(this.baselines.values());
339
+ }
340
+ /**
341
+ * Get threshold for a metric
342
+ */
343
+ getThresholdForMetric(metricName) {
344
+ // Check all threshold categories
345
+ for (const thresholds of Object.values(this.config.thresholds)) {
346
+ const match = thresholds.find(t => this.metricMatchesPattern(metricName, t.metric));
347
+ if (match)
348
+ return match;
349
+ }
350
+ return undefined;
351
+ }
352
+ /**
353
+ * Check if metric matches pattern
354
+ */
355
+ metricMatchesPattern(metricName, pattern) {
356
+ if (pattern === '*')
357
+ return true;
358
+ if (pattern.endsWith('*')) {
359
+ const prefix = pattern.slice(0, -1);
360
+ return metricName.startsWith(prefix);
361
+ }
362
+ return metricName === pattern;
363
+ }
364
+ /**
365
+ * Get regression type for metric
366
+ */
367
+ getTypeForMetric(metricName) {
368
+ if (metricName.includes('duration') || metricName.includes('latency') || metricName.includes('time')) {
369
+ return 'performance';
370
+ }
371
+ if (metricName.includes('coverage')) {
372
+ return 'coverage';
373
+ }
374
+ if (metricName.includes('flaky') || metricName.includes('stability')) {
375
+ return 'reliability';
376
+ }
377
+ if (metricName.includes('vulnerability') || metricName.includes('security')) {
378
+ return 'security';
379
+ }
380
+ if (metricName.includes('api') || metricName.includes('endpoint')) {
381
+ return 'api_breaking';
382
+ }
383
+ return 'quality';
384
+ }
385
+ /**
386
+ * Extract component name from metric and context
387
+ */
388
+ extractComponent(metricName, context) {
389
+ if (context?.file)
390
+ return String(context.file);
391
+ if (context?.test)
392
+ return String(context.test);
393
+ if (context?.endpoint)
394
+ return String(context.endpoint);
395
+ // Extract from metric name
396
+ const parts = metricName.split(/[._/]/);
397
+ return parts[0] || metricName;
398
+ }
399
+ /**
400
+ * Check if metric should be ignored
401
+ */
402
+ shouldIgnore(metricName) {
403
+ return this.config.ignorePatterns.some(pattern => this.metricMatchesPattern(metricName, pattern));
404
+ }
405
+ /**
406
+ * Create default configuration
407
+ */
408
+ createDefaultConfig() {
409
+ return {
410
+ enabled: true,
411
+ autoDetect: true,
412
+ baselineWindow: 30,
413
+ minDataPoints: 5,
414
+ method: 'zscore',
415
+ thresholds: {
416
+ performance: [
417
+ {
418
+ metric: '*_duration',
419
+ maxRegression: 20, // 20% slower
420
+ minDelta: 100, // 100ms minimum change
421
+ significanceLevel: 0.05,
422
+ direction: 'increase',
423
+ severity: 'moderate'
424
+ },
425
+ {
426
+ metric: '*_latency',
427
+ maxRegression: 15,
428
+ minDelta: 50,
429
+ significanceLevel: 0.05,
430
+ direction: 'increase',
431
+ severity: 'moderate'
432
+ }
433
+ ],
434
+ quality: [
435
+ {
436
+ metric: 'pass_rate',
437
+ maxRegression: 5, // 5% decrease
438
+ minDelta: 1,
439
+ significanceLevel: 0.05,
440
+ direction: 'decrease',
441
+ severity: 'major'
442
+ },
443
+ {
444
+ metric: 'failure_count',
445
+ maxRegression: 10, // 10% increase
446
+ minDelta: 1,
447
+ significanceLevel: 0.05,
448
+ direction: 'increase',
449
+ severity: 'moderate'
450
+ }
451
+ ],
452
+ coverage: [
453
+ {
454
+ metric: 'line_coverage',
455
+ maxRegression: 2, // 2% decrease
456
+ minDelta: 0.5,
457
+ significanceLevel: 0.1,
458
+ direction: 'decrease',
459
+ severity: 'minor'
460
+ }
461
+ ],
462
+ reliability: [
463
+ {
464
+ metric: 'flakiness_rate',
465
+ maxRegression: 5, // 5% increase
466
+ minDelta: 1,
467
+ significanceLevel: 0.05,
468
+ direction: 'increase',
469
+ severity: 'major'
470
+ }
471
+ ],
472
+ security: [
473
+ {
474
+ metric: 'vulnerability_count',
475
+ maxRegression: 0, // Any increase is critical
476
+ minDelta: 1,
477
+ significanceLevel: 0.01,
478
+ direction: 'increase',
479
+ severity: 'critical'
480
+ }
481
+ ]
482
+ },
483
+ alerts: {
484
+ enabled: true,
485
+ onNewRegression: true,
486
+ onSeverity: ['major', 'critical']
487
+ },
488
+ ignorePatterns: ['*_timestamp', '*_version', 'build_*']
489
+ };
490
+ }
491
+ }
492
+ /**
493
+ * Create a regression detector
494
+ */
495
+ export function createRegressionDetector(config) {
496
+ return new RegressionDetector(config);
497
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * QA360 Regression Detection Module
3
+ *
4
+ * Exports all regression detection functionality.
5
+ */
6
+ export type { RegressionType, RegressionSeverity, RegressionStatus, RegressionDetection, RegressionThreshold, RegressionConfig, TimeSeriesPoint, StatisticalTest, RegressionTrend, RegressionReport, Baseline } from './types.js';
7
+ export { RegressionDetector, createRegressionDetector } from './detector.js';
8
+ export { RegressionTrendAnalyzer, createRegressionTrendAnalyzer, type TrendAnalysis, type ChangePoint } from './trend-analyzer.js';
9
+ export { RegressionVault } from './vault.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * QA360 Regression Detection Module
3
+ *
4
+ * Exports all regression detection functionality.
5
+ */
6
+ // Detector
7
+ export { RegressionDetector, createRegressionDetector } from './detector.js';
8
+ // Trend Analyzer
9
+ export { RegressionTrendAnalyzer, createRegressionTrendAnalyzer } from './trend-analyzer.js';
10
+ // Vault integration
11
+ export { RegressionVault } from './vault.js';
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Regression Trend Analyzer
3
+ *
4
+ * Analyzes metric trends to predict potential regressions.
5
+ * Uses linear regression and change point detection.
6
+ */
7
+ import type { TimeSeriesPoint, RegressionTrend, RegressionDetection } from './types.js';
8
+ /**
9
+ * Trend analysis result
10
+ */
11
+ export interface TrendAnalysis {
12
+ /** Slope of trend */
13
+ slope: number;
14
+ /** Intercept */
15
+ intercept: number;
16
+ /** Correlation coefficient (R²) */
17
+ rSquared: number;
18
+ /** Is trend significant? */
19
+ significant: boolean;
20
+ /** Direction */
21
+ direction: 'increasing' | 'decreasing' | 'stable';
22
+ /** Projected value at future timestamp */
23
+ project(ms: number): number;
24
+ }
25
+ /**
26
+ * Change point detection result
27
+ */
28
+ export interface ChangePoint {
29
+ /** Index of change point */
30
+ index: number;
31
+ /** Timestamp of change */
32
+ timestamp: number;
33
+ /** Mean before change */
34
+ beforeMean: number;
35
+ /** Mean after change */
36
+ afterMean: number;
37
+ /** Magnitude of change */
38
+ magnitude: number;
39
+ /** Statistical significance */
40
+ pValue: number;
41
+ }
42
+ /**
43
+ * Regression Trend Analyzer class
44
+ */
45
+ export declare class RegressionTrendAnalyzer {
46
+ /**
47
+ * Analyze trend for a metric
48
+ */
49
+ analyzeTrend(data: TimeSeriesPoint[], windowMs?: number): RegressionTrend | null;
50
+ /**
51
+ * Perform linear regression on time series
52
+ */
53
+ linearRegression(data: TimeSeriesPoint[]): TrendAnalysis;
54
+ /**
55
+ * Detect change points in time series
56
+ */
57
+ detectChangePoints(data: TimeSeriesPoint[], minSegmentSize?: number): ChangePoint[];
58
+ /**
59
+ * Estimate p-value for change point
60
+ */
61
+ private estimatePValue;
62
+ /**
63
+ * Project trend forward
64
+ */
65
+ private projectTrend;
66
+ /**
67
+ * Get trend direction for reporting
68
+ */
69
+ private getTrendDirection;
70
+ /**
71
+ * Calculate time until threshold breach
72
+ */
73
+ calculateTimeToThreshold(data: TimeSeriesPoint[], threshold: number, isIncreasingBad: boolean): number | null;
74
+ /**
75
+ * Compare two periods for regression
76
+ */
77
+ comparePeriods(baseline: TimeSeriesPoint[], current: TimeSeriesPoint[], metric: string): RegressionDetection | null;
78
+ /**
79
+ * Infer regression type from metric name
80
+ */
81
+ private inferType;
82
+ /**
83
+ * Check if change direction indicates regression
84
+ */
85
+ private isRegressionDirection;
86
+ /**
87
+ * Normal CDF approximation
88
+ */
89
+ private normalCDF;
90
+ /**
91
+ * Get trend description
92
+ */
93
+ getTrendDescription(trend: RegressionTrend): string;
94
+ /**
95
+ * Format time to threshold
96
+ */
97
+ formatTimeToThreshold(ms: number): string;
98
+ }
99
+ /**
100
+ * Create a regression trend analyzer
101
+ */
102
+ export declare function createRegressionTrendAnalyzer(): RegressionTrendAnalyzer;