blaze-performance-tester 3.0.5 → 3.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -5,6 +5,63 @@ import { createRequire } from "module";
5
5
  import * as path from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import * as fs from "fs";
8
+
9
+ // src/LogAnalyzer.ts
10
+ var LogAnalyzer = class {
11
+ /**
12
+ * Generates a structural semantic fingerprint by masking dynamic values
13
+ */
14
+ extractLogTemplate(rawLog) {
15
+ return rawLog.trim().replace(/\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}(\.\d{3})?Z?/, "<TIMESTAMP>").replace(/0x[a-fA-F0-9]+/g, "<HEX_ID>").replace(/\b\d+\b/g, "<NUM>").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<UUID>");
16
+ }
17
+ /**
18
+ * Categorizes the structural log signature template based on runtime taxonomy rules
19
+ */
20
+ classifyTemplate(template) {
21
+ const LowercaseTpl = template.toLowerCase();
22
+ if (LowercaseTpl.includes("timeout") || LowercaseTpl.includes("econnrefused") || LowercaseTpl.includes("socket") || LowercaseTpl.includes("network") || LowercaseTpl.includes("hang up") || LowercaseTpl.includes("502") || LowercaseTpl.includes("504")) {
23
+ return { category: "Environment_Infrastructure_Error", severity: "CRITICAL" };
24
+ }
25
+ if (LowercaseTpl.includes("401") || LowercaseTpl.includes("403") || LowercaseTpl.includes("unauthorized") || LowercaseTpl.includes("forbidden") || LowercaseTpl.includes("jwt") || LowercaseTpl.includes("token expired")) {
26
+ return { category: "Authentication_Error", severity: "CRITICAL" };
27
+ }
28
+ return { category: "Product_Error", severity: "WARNING" };
29
+ }
30
+ /**
31
+ * Processes a stream of raw textual error logs into deduplicated clustered fingerprints
32
+ */
33
+ process(rawLogs) {
34
+ const clusterMap = {};
35
+ let totalCapturedErrors = 0;
36
+ for (const log of rawLogs) {
37
+ if (!log || log.trim() === "") continue;
38
+ totalCapturedErrors++;
39
+ const template = this.extractLogTemplate(log);
40
+ if (!clusterMap[template]) {
41
+ const rules = this.classifyTemplate(template);
42
+ clusterMap[template] = {
43
+ template,
44
+ count: 0,
45
+ category: rules.category,
46
+ severity: rules.severity,
47
+ examples: []
48
+ };
49
+ }
50
+ clusterMap[template].count++;
51
+ if (clusterMap[template].examples.length < 3 && !clusterMap[template].examples.includes(log)) {
52
+ clusterMap[template].examples.push(log);
53
+ }
54
+ }
55
+ const sortedClusters = Object.values(clusterMap).sort((a, b) => b.count - a.count);
56
+ return {
57
+ uniqueClustersCount: sortedClusters.length,
58
+ totalCapturedErrors,
59
+ clusters: sortedClusters
60
+ };
61
+ }
62
+ };
63
+
64
+ // cli.ts
8
65
  var __filename = fileURLToPath(import.meta.url);
9
66
  var __dirname = path.dirname(__filename);
10
67
  var require2 = createRequire(import.meta.url);
@@ -44,13 +101,15 @@ async function main() {
44
101
  }
45
102
  }
46
103
  function parseArguments(args) {
47
- const targetScript = path.resolve(args[0]);
104
+ const targetScript = path.resolve(args[0] || ".");
48
105
  const isAgentic = args.includes("--agentic");
106
+ const clusterLogs = args.includes("--cluster-logs");
49
107
  let threads = 10;
50
108
  let durationSec = 10;
51
109
  let targetApdex = 0.85;
52
110
  let targetErrorRate = 0.01;
53
111
  let maxThreads = 300;
112
+ let maxAllowedLatencyMs = 800;
54
113
  let outputHtml = "blaze-dashboard.html";
55
114
  const threadsIdx = args.indexOf("--threads");
56
115
  if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
@@ -64,16 +123,31 @@ function parseArguments(args) {
64
123
  if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
65
124
  const outputIdx = args.indexOf("--output");
66
125
  if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
126
+ if (isAgentic) {
127
+ const agenticIdx = args.indexOf("--agentic");
128
+ const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
129
+ if (trailingArgs[0]) maxThreads = parseInt(trailingArgs[0], 10);
130
+ if (trailingArgs[1]) {
131
+ let rawLatency = parseFloat(trailingArgs[1]);
132
+ if (rawLatency < 50) {
133
+ maxAllowedLatencyMs = rawLatency * 1e3;
134
+ } else {
135
+ maxAllowedLatencyMs = rawLatency;
136
+ }
137
+ }
138
+ if (trailingArgs[2]) targetErrorRate = parseFloat(trailingArgs[2]) / 100;
139
+ }
67
140
  return {
68
141
  targetScript,
69
142
  isAgentic,
143
+ clusterLogs,
70
144
  threads,
71
145
  durationSec,
72
146
  targetApdex,
73
147
  targetErrorRate,
74
148
  maxThreads,
75
149
  stepSize: 15,
76
- maxAllowedLatencyMs: 800,
150
+ maxAllowedLatencyMs,
77
151
  outputHtml
78
152
  };
79
153
  }
@@ -88,24 +162,37 @@ async function runBlazeCoreEngine(config) {
88
162
  } catch (err) {
89
163
  }
90
164
  if (!rawRequests || rawRequests.length === 0) {
91
- rawRequests = generateSimulationData(config.threads, config.durationSec);
165
+ rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs);
92
166
  }
93
- resolve2(processMetricsTelemetry(rawRequests, config.durationSec));
167
+ const metrics = processMetricsTelemetry(rawRequests, config.durationSec);
168
+ const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
169
+ resolve2({ metrics, rawErrors });
94
170
  }, 1e3);
95
171
  });
96
172
  }
97
173
  async function runIntelligentAgenticStressTest(config) {
98
174
  console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
99
- console.log(`\u{1F3AF} Target Constraints: Apdex >= ${config.targetApdex} | Error Rate <= ${config.targetErrorRate * 100}%`);
175
+ console.log(`\u{1F3AF} Target Constraints: Apdex >= ${config.targetApdex} | Error Rate <= ${(config.targetErrorRate * 100).toFixed(1)}% | SLA Target <= ${config.maxAllowedLatencyMs}ms`);
100
176
  const history = [];
101
- let currentThreads = Math.max(5, Math.floor(config.threads * 0.5));
177
+ let aggregateErrorLogs = [];
178
+ let total2xx = 0;
179
+ let total3xx = 0;
180
+ let total4xx = 0;
181
+ let total5xx = 0;
182
+ let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
102
183
  let baseStep = config.stepSize;
103
184
  let isStable = true;
104
185
  let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
105
186
  while (currentThreads <= config.maxThreads && isStable) {
106
- console.log(`
107
- \u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
108
- const metrics = await runBlazeCoreEngine({ ...config, threads: currentThreads });
187
+ console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
188
+ const { metrics, rawErrors } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
189
+ if (config.clusterLogs) {
190
+ aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
191
+ }
192
+ total2xx += metrics.c2xx;
193
+ total3xx += metrics.c3xx;
194
+ total4xx += metrics.c4xx;
195
+ total5xx += metrics.c5xx;
109
196
  const currentState = {
110
197
  threads: currentThreads,
111
198
  avgLatency: metrics.avgLatencyMs,
@@ -124,7 +211,6 @@ async function runIntelligentAgenticStressTest(config) {
124
211
  if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
125
212
  verdictReason = "The primary breakdown point was identified due to infrastructure saturation where throughput flattened while latency spiked rapidly.";
126
213
  console.log(`\u26A0\uFE0F [Agent Alert]: ${verdictReason} Backing off.`);
127
- currentThreads = Math.floor(currentThreads * 0.8);
128
214
  isStable = false;
129
215
  break;
130
216
  }
@@ -134,9 +220,9 @@ async function runIntelligentAgenticStressTest(config) {
134
220
  baseStep = Math.max(2, Math.floor(baseStep * 0.35));
135
221
  }
136
222
  }
137
- if (currentState.apdex < config.targetApdex) {
223
+ if (currentState.p95Latency > config.maxAllowedLatencyMs || currentState.apdex < config.targetApdex) {
138
224
  verdictReason = "The primary breakdown point was identified due to latency metrics breaching target limits and driving Apdex scores below thresholds.";
139
- console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. Apdex: ${currentState.apdex.toFixed(2)}`);
225
+ console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. P95 Latency: ${currentState.p95Latency.toFixed(1)}ms | Apdex: ${currentState.apdex.toFixed(2)}`);
140
226
  isStable = false;
141
227
  break;
142
228
  }
@@ -146,18 +232,32 @@ async function runIntelligentAgenticStressTest(config) {
146
232
  isStable = false;
147
233
  break;
148
234
  }
235
+ if (currentThreads === config.maxThreads) {
236
+ break;
237
+ }
149
238
  const apdexMargin = (currentState.apdex - config.targetApdex) / (1 - config.targetApdex);
150
- const adaptiveMultiplier = Math.max(0.15, Math.min(2, apdexMargin));
151
- const nextStep = Math.max(2, Math.floor(baseStep * adaptiveMultiplier));
239
+ const adaptiveMultiplier = Math.max(0.15, Math.min(2, isNaN(apdexMargin) ? 1 : apdexMargin));
240
+ let nextStep = Math.max(2, Math.floor(baseStep * adaptiveMultiplier));
241
+ if (currentThreads + nextStep > config.maxThreads) {
242
+ nextStep = config.maxThreads - currentThreads;
243
+ }
152
244
  console.log(`\u{1F4C8} [Step Adjustment]: Scaling factor ${adaptiveMultiplier.toFixed(2)}x. Next jump: +${nextStep} threads.`);
153
245
  currentThreads += nextStep;
154
246
  }
247
+ let semanticReport = null;
248
+ if (config.clusterLogs && aggregateErrorLogs.length > 0) {
249
+ console.log(`
250
+ \u{1F9E0} [Semantic Clustering]: Executing classification analyzer across ${aggregateErrorLogs.length} error entries...`);
251
+ const analyzer = new LogAnalyzer();
252
+ semanticReport = analyzer.process(aggregateErrorLogs);
253
+ console.log(`\u2705 Analysis Complete. Identified ${semanticReport.uniqueClustersCount} distinct signature blueprints.`);
254
+ }
155
255
  generateFinalAgentReport(history);
156
- exportHtmlDashboard(history, config, verdictReason);
256
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total2xx, total3xx, total4xx, total5xx });
157
257
  }
158
258
  async function runStandardStressTest(config) {
159
259
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
160
- const metrics = await runBlazeCoreEngine(config);
260
+ const { metrics, rawErrors } = await runBlazeCoreEngine(config);
161
261
  printMatrixDashboard(metrics, config.threads);
162
262
  const history = [{
163
263
  threads: config.threads,
@@ -167,25 +267,49 @@ async function runStandardStressTest(config) {
167
267
  apdex: metrics.apdexScore,
168
268
  throughput: metrics.requestsPerSecond
169
269
  }];
270
+ let semanticReport = null;
271
+ if (config.clusterLogs && rawErrors.length > 0) {
272
+ const analyzer = new LogAnalyzer();
273
+ semanticReport = analyzer.process(rawErrors);
274
+ }
170
275
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
171
276
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
172
- exportHtmlDashboard(history, config, verdictReason);
277
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, {
278
+ total2xx: metrics.c2xx,
279
+ total3xx: metrics.c3xx,
280
+ total4xx: metrics.c4xx,
281
+ total5xx: metrics.c5xx
282
+ });
173
283
  }
174
- function generateSimulationData(threads, durationSec) {
284
+ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
175
285
  const data = [];
176
- const totalRequests = threads * durationSec * 45;
286
+ const totalRequests = Math.max(15, threads * durationSec * 30);
177
287
  const startTime = Date.now();
178
- const stressFactor = threads > 120 ? Math.pow(threads / 120, 2.2) : 1;
288
+ const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
289
+ const isBreached = threads > targetThresholdBreak;
290
+ const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
291
+ const errorPool = [
292
+ `Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1605:16)`,
293
+ `HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms`,
294
+ `TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (${path.sep}app${path.sep}dist${path.sep}handler.js:42:19)`
295
+ ];
179
296
  for (let i = 0; i < totalRequests; i++) {
180
- const isError = Math.random() < (threads > 180 ? 0.08 : 2e-3);
297
+ const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
181
298
  const baseLatency = (25 + Math.random() * 35) * stressFactor;
299
+ let errorMessage;
300
+ let statusCode = 200;
301
+ if (isError) {
302
+ statusCode = Math.random() > 0.3 ? 500 : 404;
303
+ errorMessage = `[${(/* @__PURE__ */ new Date()).toISOString()}] ` + errorPool[Math.floor(Math.random() * errorPool.length)];
304
+ }
182
305
  data.push({
183
306
  durationMs: isError ? baseLatency * 0.1 : baseLatency,
184
307
  timestampMs: startTime + Math.random() * (durationSec * 1e3),
185
308
  dnsTimeMs: 1 + Math.random() * 4,
186
309
  tcpTimeMs: 5 + Math.random() * 10,
187
310
  tlsTimeMs: 10 + Math.random() * 12,
188
- statusCode: isError ? 500 : 200
311
+ statusCode,
312
+ errorMessage
189
313
  });
190
314
  }
191
315
  return data.sort((a, b) => a.timestampMs - b.timestampMs);
@@ -196,8 +320,15 @@ function processMetricsTelemetry(requests, durationSec) {
196
320
  const avgLatencyMs = mathUtils.mean(durations);
197
321
  const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
198
322
  const p95LatencyMs = mathUtils.percentile(durations, 95);
199
- const errorCount = requests.filter((r) => r.statusCode >= 400).length;
200
- const errorRate = errorCount / totalRequests;
323
+ let c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
324
+ requests.forEach((r) => {
325
+ if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
326
+ else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
327
+ else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
328
+ else if (r.statusCode >= 500) c5xx++;
329
+ });
330
+ const errorCount = c4xx + c5xx;
331
+ const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
201
332
  const T = 100;
202
333
  const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
203
334
  const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
@@ -209,7 +340,11 @@ function processMetricsTelemetry(requests, durationSec) {
209
340
  stdDevMs,
210
341
  p95LatencyMs,
211
342
  errorRate,
212
- apdexScore
343
+ apdexScore,
344
+ c2xx,
345
+ c3xx,
346
+ c4xx,
347
+ c5xx
213
348
  };
214
349
  }
215
350
  function printMatrixDashboard(m, threads) {
@@ -217,9 +352,7 @@ function printMatrixDashboard(m, threads) {
217
352
  console.log(`-----------------------------------------------------------------------------------------`);
218
353
  console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
219
354
  console.log(`-----------------------------------------------------------------------------------------`);
220
- console.log(
221
- `| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`
222
- );
355
+ console.log(`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`);
223
356
  console.log(`-----------------------------------------------------------------------------------------`);
224
357
  }
225
358
  function generateFinalAgentReport(history) {
@@ -235,7 +368,7 @@ function generateFinalAgentReport(history) {
235
368
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
236
369
  console.log("=======================================================\n");
237
370
  }
238
- function exportHtmlDashboard(history, config, verdictReason) {
371
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix) {
239
372
  const labels = history.map((h) => `${h.threads} Threads`);
240
373
  const throughputData = history.map((h) => h.throughput.toFixed(0));
241
374
  const latencyData = history.map((h) => h.avgLatency.toFixed(1));
@@ -243,9 +376,45 @@ function exportHtmlDashboard(history, config, verdictReason) {
243
376
  const waveListItems = history.map((h) => {
244
377
  return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
245
378
  }).join("");
246
- const htmlContent = `<!DOCTYPE html>
247
- <html lang="en">
248
- <head>
379
+ let logClustersHtml = "";
380
+ if (semanticReport) {
381
+ logClustersHtml = `
382
+ <div class="card" style="margin-top: 2rem;">
383
+ <h3>\u{1F9E0} Semantic Log Clustering & Error Classification</h3>
384
+ <p style="color: #94a3b8; font-size: 0.9rem;">Captured <strong>${semanticReport.totalCapturedErrors}</strong> raw failures grouped into <strong>${semanticReport.uniqueClustersCount}</strong> unique structural patterns.</p>
385
+ <table>
386
+ <thead>
387
+ <tr>
388
+ <th style="width: 10%;">Count</th>
389
+ <th style="width: 25%;">Classification Category</th>
390
+ <th style="width: 10%;">Severity</th>
391
+ <th style="width: 55%;">Structural Abstract Blueprint / Dynamic Log Fingerprint</th>
392
+ </tr>
393
+ </thead>
394
+ <tbody>
395
+ ${semanticReport.clusters.map((c) => {
396
+ let catColor = "#f87171";
397
+ if (c.category === "Environment_Infrastructure_Error") catColor = "#fb923c";
398
+ if (c.category === "Authentication_Error") catColor = "#c084fc";
399
+ let sevColor = c.severity === "CRITICAL" ? "#dc2626" : "#eab308";
400
+ return `
401
+ <tr>
402
+ <td><span style="font-size: 1.1rem; font-weight: bold; color: #f8fafc;">${c.count}</span></td>
403
+ <td><span style="color: ${catColor}; font-weight: 600;">${c.category}</span></td>
404
+ <td><span style="background: ${sevColor}; color: #fff; padding: 0.15rem 0.4rem; border-radius:3px; font-size:0.75rem; font-weight:bold;">${c.severity}</span></td>
405
+ <td>
406
+ <code style="color: #cbd5e1; background: #0f172a; padding: 0.3rem 0.5rem; display: block; border-radius: 4px; font-size: 0.85rem; word-break: break-all;">${c.template}</code>
407
+ <div style="margin-top: 0.4rem; font-size: 0.8rem; color: #64748b;">
408
+ <strong>Real Example Trace:</strong> <span style="font-family: monospace;">${c.examples[0] || ""}</span>
409
+ </div>
410
+ </td>
411
+ </tr>`;
412
+ }).join("")}
413
+ </tbody>
414
+ </table>
415
+ </div>`;
416
+ }
417
+ const htmlContent = `<!DOCTYPE html><html lang="en"><head>
249
418
  <meta charset="UTF-8">
250
419
  <title>Blaze Core Performance Report</title>
251
420
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
@@ -255,14 +424,13 @@ function exportHtmlDashboard(history, config, verdictReason) {
255
424
  .header { border-bottom: 1px solid #334155; padding-bottom: 1rem; margin-bottom: 1.5rem; }
256
425
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
257
426
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
258
-
259
- /* \u{1F916} AI VERDICT COMPONENT STYLING */
427
+ .top-layout-grid { display: grid; grid-template-columns: 1fr 450px; gap: 2rem; margin-bottom: 2rem; }
260
428
  .verdict-box {
261
429
  background: #111827;
262
430
  border: 1px dashed #ea580c;
263
431
  border-radius: 8px;
264
432
  padding: 1.25rem;
265
- margin-bottom: 2rem;
433
+ height: fit-content;
266
434
  }
267
435
  .verdict-title {
268
436
  text-transform: uppercase;
@@ -291,35 +459,108 @@ function exportHtmlDashboard(history, config, verdictReason) {
291
459
  .wave-item {
292
460
  margin: 0.25rem 0;
293
461
  }
462
+
463
+ /* \u{1F4CB} HTTP RESPONSE MATRIX BOX */
464
+ .matrix-box {
465
+ background: #1e293b;
466
+ border: 1px solid #334155;
467
+ border-radius: 8px;
468
+ padding: 1.5rem;
469
+ }
470
+ .matrix-title {
471
+ font-size: 1.15rem;
472
+ font-weight: bold;
473
+ color: #ffffff;
474
+ margin-bottom: 1.25rem;
475
+ display: flex;
476
+ align-items: center;
477
+ gap: 0.5rem;
478
+ }
479
+ .matrix-row {
480
+ display: flex;
481
+ justify-content: space-between;
482
+ align-items: center;
483
+ padding: 0.75rem 0;
484
+ border-bottom: 1px solid #334155;
485
+ }
486
+ .matrix-row:last-child {
487
+ border-bottom: none;
488
+ }
489
+ .matrix-label {
490
+ font-size: 0.95rem;
491
+ color: #f8fafc;
492
+ display: flex;
493
+ align-items: center;
494
+ gap: 0.5rem;
495
+ }
496
+ .matrix-badge {
497
+ font-size: 0.75rem;
498
+ font-weight: bold;
499
+ padding: 0.15rem 0.4rem;
500
+ border-radius: 4px;
501
+ }
502
+ .badge-2xx { background: rgba(22, 163, 74, 0.2); color: #4ade80; }
503
+ .badge-3xx { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; }
504
+ .badge-4xx { background: rgba(234, 179, 8, 0.2); color: #fde047; }
505
+ .badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
506
+ .matrix-value {
507
+ font-size: 1.1rem;
508
+ font-weight: bold;
509
+ color: #ffffff;
510
+ }
294
511
 
295
512
  .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); gap: 2rem; margin-bottom: 2rem; }
296
513
  .card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 1.5rem; }
297
514
  h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #334155; padding-bottom: 0.5rem; }
298
515
  table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
299
- th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #334155; }
516
+ th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #334155; vertical-align: top; }
300
517
  th { color: #94a3b8; font-weight: 600; }
301
518
  tr:hover { background: #273549; }
302
519
  .badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
303
520
  .badge-success { background: #16a34a; color: #f0fdf4; }
304
521
  .badge-fail { background: #dc2626; color: #fef2f2; }
305
- </style>
306
- </head>
307
- <body>
522
+ </style></head><body>
308
523
  <div class="container">
309
524
  <div class="header">
310
525
  <h1>\u{1F525} Blaze Core Performance Analysis</h1>
311
526
  <div class="meta">Target Script: <code>${config.targetScript}</code> | Mode: ${config.isAgentic ? "\u{1F9E0} Intelligent Agentic" : "\u{1F3CB}\uFE0F Fixed Workload"}</div>
312
527
  </div>
313
-
314
- <!-- \u{1F916} AUTOMATED VERDICT CONTAINER -->
315
- <div class="verdict-box">
316
- <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
317
- <div class="verdict-text">
318
- Blaze Agent auto-evaluated execution telemetry across <strong>${history.length} test waves</strong>.
319
- ${verdictReason}
528
+
529
+ <div class="top-layout-grid">
530
+ <div class="verdict-box">
531
+ <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
532
+ <div class="verdict-text">
533
+ Blaze Agent auto-evaluated execution telemetry across <strong>${history.length} test waves</strong>.
534
+ ${verdictReason}
535
+ </div>
536
+ <div class="verdict-waves">
537
+ ${waveListItems}
538
+ </div>
320
539
  </div>
321
- <div class="verdict-waves">
322
- ${waveListItems}
540
+
541
+ <!-- \u{1F4CB} DYNAMIC HTTP RESPONSE MATRIX COMPONENT -->
542
+ <div class="matrix-box">
543
+ <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
544
+
545
+ <div class="matrix-row">
546
+ <div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
547
+ <div class="matrix-value">${responseMatrix.total2xx}</div>
548
+ </div>
549
+
550
+ <div class="matrix-row">
551
+ <div class="matrix-label">Redirects <span class="matrix-badge badge-3xx">3xx</span></div>
552
+ <div class="matrix-value">${responseMatrix.total3xx}</div>
553
+ </div>
554
+
555
+ <div class="matrix-row">
556
+ <div class="matrix-label">Client Error <span class="matrix-badge badge-4xx">4xx</span></div>
557
+ <div class="matrix-value">${responseMatrix.total4xx}</div>
558
+ </div>
559
+
560
+ <div class="matrix-row">
561
+ <div class="matrix-label">Server Error <span class="matrix-badge badge-5xx">5xx</span></div>
562
+ <div class="matrix-value">${responseMatrix.total5xx}</div>
563
+ </div>
323
564
  </div>
324
565
  </div>
325
566
 
@@ -327,7 +568,7 @@ function exportHtmlDashboard(history, config, verdictReason) {
327
568
  <div class="card"><canvas id="throughputChart"></canvas></div>
328
569
  <div class="card"><canvas id="latencyChart"></canvas></div>
329
570
  </div>
330
-
571
+
331
572
  <div class="card">
332
573
  <h3>Telemetry Matrix Logs</h3>
333
574
  <table>
@@ -345,8 +586,7 @@ function exportHtmlDashboard(history, config, verdictReason) {
345
586
  <tbody>
346
587
  ${history.map((h) => {
347
588
  const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
348
- return `
349
- <tr>
589
+ return ` <tr>
350
590
  <td><strong>${h.threads}</strong></td>
351
591
  <td>${h.throughput.toFixed(0)} req/sec</td>
352
592
  <td>${h.avgLatency.toFixed(1)} ms</td>
@@ -363,12 +603,11 @@ function exportHtmlDashboard(history, config, verdictReason) {
363
603
  </tbody>
364
604
  </table>
365
605
  </div>
606
+ ${logClustersHtml}
366
607
  </div>
367
-
368
608
  <script>
369
609
  const labels = ${JSON.stringify(labels)};
370
-
371
- new Chart(document.getElementById('throughputChart'), {
610
+ new Chart(document.getElementById('throughputChart'), {
372
611
  type: 'line',
373
612
  data: {
374
613
  labels: labels,
@@ -383,7 +622,6 @@ function exportHtmlDashboard(history, config, verdictReason) {
383
622
  },
384
623
  options: { responsive: true, plugins: { legend: { labels: { color: '#f8fafc' } } } }
385
624
  });
386
-
387
625
  new Chart(document.getElementById('latencyChart'), {
388
626
  type: 'line',
389
627
  data: {
@@ -413,9 +651,7 @@ function exportHtmlDashboard(history, config, verdictReason) {
413
651
  }
414
652
  }
415
653
  });
416
- </script>
417
- </body>
418
- </html>`;
654
+ </script></body></html>`;
419
655
  try {
420
656
  fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
421
657
  console.log(`\u{1F4CA} Standalone Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
@@ -424,9 +660,7 @@ function exportHtmlDashboard(history, config, verdictReason) {
424
660
  }
425
661
  }
426
662
  function printUsage() {
427
- console.log(`
428
- Blaze Core Performance Test CLI Engine
429
-
663
+ console.log(`Blaze Core Performance Test CLI Engine
430
664
  Usage:
431
665
  blaze <target-script.ts> [options]
432
666
 
@@ -434,6 +668,7 @@ Options:
434
668
  --threads <count> Number of working execution thread pools (Default: 10)
435
669
  --duration <seconds> Test time frame duration (Default: 10s)
436
670
  --agentic Engages Intelligent Autonomous Adaptive Loop Testing
671
+ --cluster-logs Enables Semantic Log Clustering and Analysis
437
672
  --target-apdex <score> Target threshold cutoff limit for Apdex (Default: 0.85)
438
673
  --target-error <rate> Target cutoff threshold percentage for failures (Default: 0.01)
439
674
  --max-threads <count> Safety bounding limit cap for Agent ramp-up (Default: 300)
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.0.5",
3
+ "version": "3.0.19",
4
4
  "description": "A high-performance, multi-threaded load testing engine built with Rust and QuickJS.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",