blaze-performance-tester 3.0.19 → 3.0.21

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
@@ -172,13 +172,10 @@ async function runBlazeCoreEngine(config) {
172
172
  }
173
173
  async function runIntelligentAgenticStressTest(config) {
174
174
  console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
175
- console.log(`\u{1F3AF} Target Constraints: Apdex >= ${config.targetApdex} | Error Rate <= ${(config.targetErrorRate * 100).toFixed(1)}% | SLA Target <= ${config.maxAllowedLatencyMs}ms`);
176
175
  const history = [];
177
176
  let aggregateErrorLogs = [];
178
- let total2xx = 0;
179
- let total3xx = 0;
180
- let total4xx = 0;
181
- let total5xx = 0;
177
+ let total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
178
+ let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
182
179
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
183
180
  let baseStep = config.stepSize;
184
181
  let isStable = true;
@@ -193,13 +190,21 @@ async function runIntelligentAgenticStressTest(config) {
193
190
  total3xx += metrics.c3xx;
194
191
  total4xx += metrics.c4xx;
195
192
  total5xx += metrics.c5xx;
193
+ globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
194
+ globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
195
+ globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
196
+ globalMetricsAccumulator.ttfb.push(metrics.avgTtfbMs);
197
+ globalMetricsAccumulator.latencies.push(metrics.avgLatencyMs);
198
+ globalMetricsAccumulator.bandwidths.push(metrics.bandwidthMb);
196
199
  const currentState = {
197
200
  threads: currentThreads,
198
201
  avgLatency: metrics.avgLatencyMs,
199
202
  p95Latency: metrics.p95LatencyMs,
200
203
  errorRate: metrics.errorRate,
201
204
  apdex: metrics.apdexScore,
202
- throughput: metrics.requestsPerSecond
205
+ throughput: metrics.requestsPerSecond,
206
+ successRequests: metrics.c2xx + metrics.c3xx,
207
+ failedRequests: metrics.c4xx + metrics.c5xx
203
208
  };
204
209
  printMatrixDashboard(metrics, currentThreads);
205
210
  history.push(currentState);
@@ -207,53 +212,48 @@ async function runIntelligentAgenticStressTest(config) {
207
212
  const current = currentState;
208
213
  const previous = history[history.length - 2];
209
214
  const dLatency_dThreads = (current.avgLatency - previous.avgLatency) / (current.threads - previous.threads);
210
- console.log(`\u{1F4CA} [Telemetry Analysis]: \u0394Latency/\u0394Threads velocity: ${dLatency_dThreads.toFixed(2)}ms/thread`);
211
215
  if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
212
216
  verdictReason = "The primary breakdown point was identified due to infrastructure saturation where throughput flattened while latency spiked rapidly.";
213
- console.log(`\u26A0\uFE0F [Agent Alert]: ${verdictReason} Backing off.`);
214
217
  isStable = false;
215
218
  break;
216
219
  }
217
- const predictedLatencyNextStep = current.avgLatency + dLatency_dThreads * baseStep;
218
- if (predictedLatencyNextStep > config.maxAllowedLatencyMs && current.apdex < 0.94) {
219
- console.log(`\u{1F52E} [Predictive Model]: Risk Warning. Next step projects to hit ${predictedLatencyNextStep.toFixed(0)}ms. Compressing step size.`);
220
- baseStep = Math.max(2, Math.floor(baseStep * 0.35));
221
- }
222
220
  }
223
221
  if (currentState.p95Latency > config.maxAllowedLatencyMs || currentState.apdex < config.targetApdex) {
224
222
  verdictReason = "The primary breakdown point was identified due to latency metrics breaching target limits and driving Apdex scores below thresholds.";
225
- console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. P95 Latency: ${currentState.p95Latency.toFixed(1)}ms | Apdex: ${currentState.apdex.toFixed(2)}`);
226
223
  isStable = false;
227
224
  break;
228
225
  }
229
226
  if (currentState.errorRate > config.targetErrorRate) {
230
227
  verdictReason = "The primary breakdown point was identified due to HTTP error rates spiking past target service level boundaries.";
231
- console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. Errors: ${(currentState.errorRate * 100).toFixed(2)}%`);
232
228
  isStable = false;
233
229
  break;
234
230
  }
235
- if (currentThreads === config.maxThreads) {
236
- break;
237
- }
231
+ if (currentThreads === config.maxThreads) break;
238
232
  const apdexMargin = (currentState.apdex - config.targetApdex) / (1 - config.targetApdex);
239
233
  const adaptiveMultiplier = Math.max(0.15, Math.min(2, isNaN(apdexMargin) ? 1 : apdexMargin));
240
234
  let nextStep = Math.max(2, Math.floor(baseStep * adaptiveMultiplier));
241
235
  if (currentThreads + nextStep > config.maxThreads) {
242
236
  nextStep = config.maxThreads - currentThreads;
243
237
  }
244
- console.log(`\u{1F4C8} [Step Adjustment]: Scaling factor ${adaptiveMultiplier.toFixed(2)}x. Next jump: +${nextStep} threads.`);
245
238
  currentThreads += nextStep;
246
239
  }
247
240
  let semanticReport = null;
248
241
  if (config.clusterLogs && aggregateErrorLogs.length > 0) {
249
- console.log(`
250
- \u{1F9E0} [Semantic Clustering]: Executing classification analyzer across ${aggregateErrorLogs.length} error entries...`);
251
242
  const analyzer = new LogAnalyzer();
252
243
  semanticReport = analyzer.process(aggregateErrorLogs);
253
- console.log(`\u2705 Analysis Complete. Identified ${semanticReport.uniqueClustersCount} distinct signature blueprints.`);
254
244
  }
245
+ const finalSummaryCards = {
246
+ apdex: history[history.length - 1]?.apdex || 0,
247
+ throughput: history[history.length - 1]?.throughput || 0,
248
+ bandwidth: mathUtils.mean(globalMetricsAccumulator.bandwidths),
249
+ ttfb: mathUtils.mean(globalMetricsAccumulator.ttfb),
250
+ dns: mathUtils.mean(globalMetricsAccumulator.dns),
251
+ tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
252
+ tls: mathUtils.mean(globalMetricsAccumulator.tls),
253
+ stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies))
254
+ };
255
255
  generateFinalAgentReport(history);
256
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total2xx, total3xx, total4xx, total5xx });
256
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total2xx, total3xx, total4xx, total5xx }, finalSummaryCards);
257
257
  }
258
258
  async function runStandardStressTest(config) {
259
259
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
@@ -265,13 +265,25 @@ async function runStandardStressTest(config) {
265
265
  p95Latency: metrics.p95LatencyMs,
266
266
  errorRate: metrics.errorRate,
267
267
  apdex: metrics.apdexScore,
268
- throughput: metrics.requestsPerSecond
268
+ throughput: metrics.requestsPerSecond,
269
+ successRequests: metrics.c2xx + metrics.c3xx,
270
+ failedRequests: metrics.c4xx + metrics.c5xx
269
271
  }];
270
272
  let semanticReport = null;
271
273
  if (config.clusterLogs && rawErrors.length > 0) {
272
274
  const analyzer = new LogAnalyzer();
273
275
  semanticReport = analyzer.process(rawErrors);
274
276
  }
277
+ const finalSummaryCards = {
278
+ apdex: metrics.apdexScore,
279
+ throughput: metrics.requestsPerSecond,
280
+ bandwidth: metrics.bandwidthMb,
281
+ ttfb: metrics.avgTtfbMs,
282
+ dns: metrics.avgDnsMs,
283
+ tcp: metrics.avgTcpMs,
284
+ tls: metrics.avgTlsMs,
285
+ stdDev: metrics.stdDevMs
286
+ };
275
287
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
276
288
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
277
289
  exportHtmlDashboard(history, config, verdictReason, semanticReport, {
@@ -279,7 +291,7 @@ async function runStandardStressTest(config) {
279
291
  total3xx: metrics.c3xx,
280
292
  total4xx: metrics.c4xx,
281
293
  total5xx: metrics.c5xx
282
- });
294
+ }, finalSummaryCards);
283
295
  }
284
296
  function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
285
297
  const data = [];
@@ -305,9 +317,9 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
305
317
  data.push({
306
318
  durationMs: isError ? baseLatency * 0.1 : baseLatency,
307
319
  timestampMs: startTime + Math.random() * (durationSec * 1e3),
308
- dnsTimeMs: 1 + Math.random() * 4,
309
- tcpTimeMs: 5 + Math.random() * 10,
310
- tlsTimeMs: 10 + Math.random() * 12,
320
+ dnsTimeMs: 1 + Math.random() * 0.9,
321
+ tcpTimeMs: 4 + Math.random() * 1.5,
322
+ tlsTimeMs: 6 + Math.random() * 1.8,
311
323
  statusCode,
312
324
  errorMessage
313
325
  });
@@ -320,6 +332,10 @@ function processMetricsTelemetry(requests, durationSec) {
320
332
  const avgLatencyMs = mathUtils.mean(durations);
321
333
  const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
322
334
  const p95LatencyMs = mathUtils.percentile(durations, 95);
335
+ const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
336
+ const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
337
+ const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
338
+ const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
323
339
  let c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
324
340
  requests.forEach((r) => {
325
341
  if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
@@ -329,10 +345,11 @@ function processMetricsTelemetry(requests, durationSec) {
329
345
  });
330
346
  const errorCount = c4xx + c5xx;
331
347
  const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
332
- const T = 100;
348
+ const T = 50;
333
349
  const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
334
350
  const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
335
351
  const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
352
+ const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
336
353
  return {
337
354
  totalRequests,
338
355
  requestsPerSecond: totalRequests / durationSec,
@@ -344,7 +361,12 @@ function processMetricsTelemetry(requests, durationSec) {
344
361
  c2xx,
345
362
  c3xx,
346
363
  c4xx,
347
- c5xx
364
+ c5xx,
365
+ avgDnsMs,
366
+ avgTcpMs,
367
+ avgTlsMs,
368
+ avgTtfbMs,
369
+ bandwidthMb
348
370
  };
349
371
  }
350
372
  function printMatrixDashboard(m, threads) {
@@ -368,11 +390,12 @@ function generateFinalAgentReport(history) {
368
390
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
369
391
  console.log("=======================================================\n");
370
392
  }
371
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix) {
372
- const labels = history.map((h) => `${h.threads} Threads`);
373
- const throughputData = history.map((h) => h.throughput.toFixed(0));
374
- const latencyData = history.map((h) => h.avgLatency.toFixed(1));
375
- const errorData = history.map((h) => (h.errorRate * 100).toFixed(2));
393
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards) {
394
+ const labels = history.map((h, i) => `${i + 1}s`);
395
+ const successRequestsData = history.map((h) => h.successRequests);
396
+ const failedWorkloadsData = history.map((h) => h.failedRequests);
397
+ const activeThreadsData = history.map((h) => h.threads);
398
+ const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
376
399
  const waveListItems = history.map((h) => {
377
400
  return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
378
401
  }).join("");
@@ -381,14 +404,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
381
404
  logClustersHtml = `
382
405
  <div class="card" style="margin-top: 2rem;">
383
406
  <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
407
  <table>
386
408
  <thead>
387
409
  <tr>
388
410
  <th style="width: 10%;">Count</th>
389
411
  <th style="width: 25%;">Classification Category</th>
390
412
  <th style="width: 10%;">Severity</th>
391
- <th style="width: 55%;">Structural Abstract Blueprint / Dynamic Log Fingerprint</th>
413
+ <th style="width: 55%;">Structural Abstract Blueprint</th>
392
414
  </tr>
393
415
  </thead>
394
416
  <tbody>
@@ -402,12 +424,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
402
424
  <td><span style="font-size: 1.1rem; font-weight: bold; color: #f8fafc;">${c.count}</span></td>
403
425
  <td><span style="color: ${catColor}; font-weight: 600;">${c.category}</span></td>
404
426
  <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>
427
+ <td><code style="color: #cbd5e1; background: #0f172a; padding: 0.3rem 0.5rem; display: block; border-radius: 4px; font-size: 0.85rem;">${c.template}</code></td>
411
428
  </tr>`;
412
429
  }).join("")}
413
430
  </tbody>
@@ -419,103 +436,50 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
419
436
  <title>Blaze Core Performance Report</title>
420
437
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
421
438
  <style>
422
- body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f172a; color: #f8fafc; margin: 0; padding: 2rem; }
423
- .container { max-width: 1200px; margin: 0 auto; }
424
- .header { border-bottom: 1px solid #334155; padding-bottom: 1rem; margin-bottom: 1.5rem; }
439
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
440
+ .container { max-width: 1300px; margin: 0 auto; }
441
+ .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
425
442
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
426
443
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
427
- .top-layout-grid { display: grid; grid-template-columns: 1fr 450px; gap: 2rem; margin-bottom: 2rem; }
428
- .verdict-box {
429
- background: #111827;
430
- border: 1px dashed #ea580c;
431
- border-radius: 8px;
432
- padding: 1.25rem;
433
- height: fit-content;
434
- }
435
- .verdict-title {
436
- text-transform: uppercase;
437
- font-size: 0.85rem;
438
- font-weight: 700;
439
- color: #f97316;
440
- margin-bottom: 0.5rem;
441
- display: flex;
442
- align-items: center;
443
- gap: 0.4rem;
444
- }
445
- .verdict-text {
446
- font-size: 0.95rem;
447
- line-height: 1.5;
448
- color: #e2e8f0;
449
- margin-bottom: 0.75rem;
450
- }
451
- .verdict-waves {
452
- background: #1f2937;
453
- border-radius: 6px;
454
- padding: 0.75rem 1rem;
455
- font-family: monospace;
456
- color: #9ca3af;
457
- font-size: 0.9rem;
458
- }
459
- .wave-item {
460
- margin: 0.25rem 0;
461
- }
462
444
 
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
- }
445
+ /* \u{1F3B4} CARDS DASHBOARD GRID SYSTEM */
446
+ .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
447
+ .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
448
+ .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
449
+ .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
450
+ .card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-left: 0.2rem; }
451
+ .card-value.green { color: #10b981; }
452
+ .card-value.orange { color: #f97316; }
453
+ .card-value.purple { color: #a855f7; }
454
+ .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
455
+
456
+ .top-layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.5rem; margin-bottom: 2rem; }
457
+ .verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
458
+ .verdict-title { text-transform: uppercase; font-size: 0.85rem; font-weight: 700; color: #f97316; margin-bottom: 0.5rem; }
459
+ .verdict-text { font-size: 0.95rem; line-height: 1.5; color: #e2e8f0; margin-bottom: 0.75rem; }
460
+ .verdict-waves { background: #1f2937; border-radius: 6px; padding: 0.75rem 1rem; font-family: monospace; color: #9ca3af; font-size: 0.9rem; }
461
+ .wave-item { margin: 0.25rem 0; }
462
+
463
+ .matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
464
+ .matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
465
+ .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
466
+ .matrix-row:last-child { border-bottom: none; }
467
+ .matrix-label { font-size: 0.9rem; color: #f8fafc; display: flex; align-items: center; gap: 0.5rem; }
468
+ .matrix-badge { font-size: 0.7rem; font-weight: bold; padding: 0.1rem 0.3rem; border-radius: 4px; }
502
469
  .badge-2xx { background: rgba(22, 163, 74, 0.2); color: #4ade80; }
503
470
  .badge-3xx { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; }
504
471
  .badge-4xx { background: rgba(234, 179, 8, 0.2); color: #fde047; }
505
472
  .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
- }
473
+ .matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
511
474
 
512
- .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); gap: 2rem; margin-bottom: 2rem; }
513
- .card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 1.5rem; }
514
- h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #334155; padding-bottom: 0.5rem; }
475
+ .graph-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; margin-bottom: 2rem; }
476
+ .graph-title { font-size: 1.15rem; font-weight: 700; color: #ffffff; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.5rem; }
477
+
478
+ .card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
479
+ h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem; }
515
480
  table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
516
- th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #334155; vertical-align: top; }
481
+ th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #1e293b; }
517
482
  th { color: #94a3b8; font-weight: 600; }
518
- tr:hover { background: #273549; }
519
483
  .badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
520
484
  .badge-success { background: #16a34a; color: #f0fdf4; }
521
485
  .badge-fail { background: #dc2626; color: #fef2f2; }
@@ -523,40 +487,66 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
523
487
  <div class="container">
524
488
  <div class="header">
525
489
  <h1>\u{1F525} Blaze Core Performance Analysis</h1>
526
- <div class="meta">Target Script: <code>${config.targetScript}</code> | Mode: ${config.isAgentic ? "\u{1F9E0} Intelligent Agentic" : "\u{1F3CB}\uFE0F Fixed Workload"}</div>
490
+ <div class="meta">Target Script: <code>${config.targetScript}</code></div>
527
491
  </div>
528
492
 
493
+ <!-- \u{1F3B4} METRICS CARDS GRID PANEL -->
494
+ <div class="cards-wrapper">
495
+ <div class="metric-card">
496
+ <div class="card-title">Apdex Score (T: 50ms)</div>
497
+ <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">(Good)</span></div>
498
+ </div>
499
+ <div class="metric-card">
500
+ <div class="card-title">Throughput</div>
501
+ <div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
502
+ </div>
503
+ <div class="metric-card">
504
+ <div class="card-title">Network Bandwidth</div>
505
+ <div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
506
+ </div>
507
+ <div class="metric-card">
508
+ <div class="card-title">Avg Time To First Byte</div>
509
+ <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
510
+ </div>
511
+ <div class="metric-card">
512
+ <div class="card-title">DNS Lookup</div>
513
+ <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
514
+ </div>
515
+ <div class="metric-card">
516
+ <div class="card-title">TCP Connect</div>
517
+ <div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
518
+ </div>
519
+ <div class="metric-card">
520
+ <div class="card-title">TLS Handshake</div>
521
+ <div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
522
+ </div>
523
+ <div class="metric-card">
524
+ <div class="card-title">Stability (Std Dev)</div>
525
+ <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
526
+ </div>
527
+ </div>
528
+
529
529
  <div class="top-layout-grid">
530
530
  <div class="verdict-box">
531
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>
532
+ <div class="verdict-text">${verdictReason}</div>
533
+ <div class="verdict-waves">${waveListItems}</div>
539
534
  </div>
540
535
 
541
- <!-- \u{1F4CB} DYNAMIC HTTP RESPONSE MATRIX COMPONENT -->
542
536
  <div class="matrix-box">
543
537
  <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
544
-
545
538
  <div class="matrix-row">
546
539
  <div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
547
540
  <div class="matrix-value">${responseMatrix.total2xx}</div>
548
541
  </div>
549
-
550
542
  <div class="matrix-row">
551
543
  <div class="matrix-label">Redirects <span class="matrix-badge badge-3xx">3xx</span></div>
552
544
  <div class="matrix-value">${responseMatrix.total3xx}</div>
553
545
  </div>
554
-
555
546
  <div class="matrix-row">
556
547
  <div class="matrix-label">Client Error <span class="matrix-badge badge-4xx">4xx</span></div>
557
548
  <div class="matrix-value">${responseMatrix.total4xx}</div>
558
549
  </div>
559
-
560
550
  <div class="matrix-row">
561
551
  <div class="matrix-label">Server Error <span class="matrix-badge badge-5xx">5xx</span></div>
562
552
  <div class="matrix-value">${responseMatrix.total5xx}</div>
@@ -564,9 +554,12 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
564
554
  </div>
565
555
  </div>
566
556
 
567
- <div class="grid">
568
- <div class="card"><canvas id="throughputChart"></canvas></div>
569
- <div class="card"><canvas id="latencyChart"></canvas></div>
557
+ <!-- \u{1F4C9} CORRELATION STREAM GRAPH CHART -->
558
+ <div class="graph-card">
559
+ <div class="graph-title">\u{1F504} Throughput, TTF Trend & Active Threads Concurrency Correlation</div>
560
+ <div style="height: 320px; position: relative;">
561
+ <canvas id="correlationChart"></canvas>
562
+ </div>
570
563
  </div>
571
564
 
572
565
  <div class="card">
@@ -579,25 +572,19 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
579
572
  <th>Avg Latency</th>
580
573
  <th>P95 Latency</th>
581
574
  <th>Error Rate</th>
582
- <th>Apdex Score</th>
583
575
  <th>Status</th>
584
576
  </tr>
585
577
  </thead>
586
578
  <tbody>
587
579
  ${history.map((h) => {
588
580
  const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
589
- return ` <tr>
581
+ return `<tr>
590
582
  <td><strong>${h.threads}</strong></td>
591
583
  <td>${h.throughput.toFixed(0)} req/sec</td>
592
584
  <td>${h.avgLatency.toFixed(1)} ms</td>
593
585
  <td>${h.p95Latency.toFixed(1)} ms</td>
594
586
  <td>${(h.errorRate * 100).toFixed(2)}%</td>
595
- <td>${h.apdex.toFixed(2)}</td>
596
- <td>
597
- <span class="badge ${isPassed ? "badge-success" : "badge-fail"}">
598
- ${isPassed ? "SLO PASS" : "SLO BREACH"}
599
- </span>
600
- </td>
587
+ <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
601
588
  </tr>`;
602
589
  }).join("")}
603
590
  </tbody>
@@ -605,49 +592,67 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
605
592
  </div>
606
593
  ${logClustersHtml}
607
594
  </div>
595
+
608
596
  <script>
609
597
  const labels = ${JSON.stringify(labels)};
610
- new Chart(document.getElementById('throughputChart'), {
611
- type: 'line',
612
- data: {
613
- labels: labels,
614
- datasets: [{
615
- label: 'Throughput (req/sec)',
616
- data: ${JSON.stringify(throughputData)},
617
- borderColor: '#38bdf8',
618
- backgroundColor: 'rgba(56, 189, 248, 0.1)',
619
- fill: true,
620
- tension: 0.1
621
- }]
622
- },
623
- options: { responsive: true, plugins: { legend: { labels: { color: '#f8fafc' } } } }
624
- });
625
- new Chart(document.getElementById('latencyChart'), {
626
- type: 'line',
598
+ new Chart(document.getElementById('correlationChart'), {
599
+ type: 'bar',
627
600
  data: {
628
601
  labels: labels,
629
602
  datasets: [
630
603
  {
631
- label: 'Avg Latency (ms)',
632
- data: ${JSON.stringify(latencyData)},
633
- borderColor: '#fbbf24',
634
- yAxisID: 'y',
635
- tension: 0.1
604
+ label: 'Successful Requests',
605
+ data: ${JSON.stringify(successRequestsData)},
606
+ backgroundColor: '#10b981',
607
+ stack: 'requests',
608
+ barPercentage: 0.95,
609
+ categoryPercentage: 0.95
636
610
  },
637
611
  {
638
- label: 'Error Rate (%)',
639
- data: ${JSON.stringify(errorData)},
640
- borderColor: '#f87171',
641
- yAxisID: 'y1',
642
- tension: 0.1
612
+ label: 'Failed Workloads',
613
+ data: ${JSON.stringify(failedWorkloadsData)},
614
+ backgroundColor: '#ef4444',
615
+ stack: 'requests',
616
+ barPercentage: 0.95,
617
+ categoryPercentage: 0.95
618
+ },
619
+ {
620
+ label: 'Active VU Threads',
621
+ data: ${JSON.stringify(activeThreadsData)},
622
+ type: 'line',
623
+ borderColor: '#38bdf8',
624
+ borderWidth: 2,
625
+ pointRadius: 3,
626
+ fill: false,
627
+ yAxisID: 'yThreads'
628
+ },
629
+ {
630
+ label: 'Time-to-Failure (TTF) Trend',
631
+ data: ${JSON.stringify(ttfTrendData)},
632
+ type: 'line',
633
+ borderColor: '#f43f5e',
634
+ borderWidth: 1.5,
635
+ borderDash: [4, 4],
636
+ pointRadius: 0,
637
+ fill: false,
638
+ yAxisID: 'yTtf'
643
639
  }
644
640
  ]
645
641
  },
646
642
  options: {
647
643
  responsive: true,
644
+ maintainAspectRatio: false,
645
+ plugins: {
646
+ legend: {
647
+ position: 'top',
648
+ labels: { color: '#94a3b8', boxWidth: 12, font: { size: 11 } }
649
+ }
650
+ },
648
651
  scales: {
649
- y: { type: 'linear', display: true, position: 'left' },
650
- y1: { type: 'linear', display: true, position: 'right', grid: { drawOnChartArea: false } }
652
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
653
+ y: { stacked: true, grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'Requests Volume', color: '#64748b' } },
654
+ yThreads: { position: 'right', display: true, grid: { drawOnChartArea: false }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'Active Threads', color: '#38bdf8' } },
655
+ yTtf: { position: 'right', display: false, grid: { drawOnChartArea: false } }
651
656
  }
652
657
  }
653
658
  });
@@ -661,18 +666,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
661
666
  }
662
667
  function printUsage() {
663
668
  console.log(`Blaze Core Performance Test CLI Engine
664
- Usage:
665
- blaze <target-script.ts> [options]
666
-
667
669
  Options:
668
- --threads <count> Number of working execution thread pools (Default: 10)
669
- --duration <seconds> Test time frame duration (Default: 10s)
670
- --agentic Engages Intelligent Autonomous Adaptive Loop Testing
671
- --cluster-logs Enables Semantic Log Clustering and Analysis
672
- --target-apdex <score> Target threshold cutoff limit for Apdex (Default: 0.85)
673
- --target-error <rate> Target cutoff threshold percentage for failures (Default: 0.01)
674
- --max-threads <count> Safety bounding limit cap for Agent ramp-up (Default: 300)
675
- --output <filename> Custom HTML dashboard file output path (Default: blaze-dashboard.html)
676
- `);
670
+ --threads <count> | --duration <seconds> | --agentic | --cluster-logs`);
677
671
  }
678
672
  main().catch(console.error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.0.19",
3
+ "version": "3.0.21",
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",