blaze-performance-tester 3.1.21 → 3.1.23

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
@@ -240,6 +240,13 @@ function calculateSeoImpactMetrics(avgLatencyMs) {
240
240
  }
241
241
  return { trafficLoss, status, crawlPenalty, color };
242
242
  }
243
+ function computeHealthScoreValue(apdex, errorRate, p95Latency, maxAllowedLatency) {
244
+ const apdexComponent = apdex * 50;
245
+ const errorComponent = Math.max(0, 1 - errorRate) * 30;
246
+ const latencyRatio = p95Latency / maxAllowedLatency;
247
+ const latencyComponent = Math.max(0, 1 - Math.min(1, latencyRatio)) * 20;
248
+ return Math.min(100, Math.max(0, Math.round(apdexComponent + errorComponent + latencyComponent)));
249
+ }
243
250
  async function runIntelligentAgenticStressTest(config) {
244
251
  console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
245
252
  const history = [];
@@ -327,20 +334,23 @@ async function runIntelligentAgenticStressTest(config) {
327
334
  const analyzer = new LogAnalyzer();
328
335
  semanticReport = analyzer.process(aggregateErrorLogs);
329
336
  }
337
+ const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0 };
338
+ const healthScore = computeHealthScoreValue(finalState.apdex, finalState.errorRate, finalState.p95Latency, config.maxAllowedLatencyMs);
330
339
  const finalSummaryCards = {
331
- apdex: history[history.length - 1]?.apdex || 0,
332
- throughput: history[history.length - 1]?.throughput || 0,
340
+ apdex: finalState.apdex,
341
+ throughput: finalState.throughput,
333
342
  bandwidth: mathUtils.mean(globalMetricsAccumulator.bandwidths),
334
343
  ttfb: mathUtils.mean(globalMetricsAccumulator.ttfb),
335
344
  dns: mathUtils.mean(globalMetricsAccumulator.dns),
336
345
  tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
337
346
  tls: mathUtils.mean(globalMetricsAccumulator.tls),
338
347
  stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
339
- saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50
348
+ saturationKneePoint: saturationKneePoint || finalState.threads || 50,
349
+ healthScore
340
350
  };
341
- generateFinalAgentReport(history);
351
+ generateFinalAgentReport(history, healthScore);
342
352
  if (config.isSeo) {
343
- const finalLat = history[history.length - 1]?.avgLatency || finalSummaryCards.ttfb;
353
+ const finalLat = finalState.avgLatency || finalSummaryCards.ttfb;
344
354
  const seo = calculateSeoImpactMetrics(finalLat);
345
355
  console.log(`\u{1F50E} [SEO Impact Report]: Target latency under stress (${finalLat.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
346
356
  }
@@ -354,6 +364,8 @@ async function runStandardStressTest(config) {
354
364
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
355
365
  const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
356
366
  printMatrixDashboard(metrics, config.threads);
367
+ const healthScore = computeHealthScoreValue(metrics.apdexScore, metrics.errorRate, metrics.p95LatencyMs, config.maxAllowedLatencyMs);
368
+ console.log(`\u{1F49A} Unified Blaze Health Score: ${healthScore}/100`);
357
369
  const history = [{
358
370
  threads: config.threads,
359
371
  avgLatency: metrics.avgLatencyMs,
@@ -380,7 +392,8 @@ async function runStandardStressTest(config) {
380
392
  tcp: metrics.avgTcpMs,
381
393
  tls: metrics.avgTlsMs,
382
394
  stdDev: metrics.stdDevMs,
383
- saturationKneePoint: config.threads
395
+ saturationKneePoint: config.threads,
396
+ healthScore
384
397
  };
385
398
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
386
399
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
@@ -501,7 +514,7 @@ function printMatrixDashboard(m, threads) {
501
514
  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)} |`);
502
515
  console.log(`-----------------------------------------------------------------------------------------`);
503
516
  }
504
- function generateFinalAgentReport(history) {
517
+ function generateFinalAgentReport(history, score) {
505
518
  if (history.length === 0) return;
506
519
  const peakThroughput = Math.max(...history.map((h) => h.throughput));
507
520
  const cleanRuns = history.filter((h) => h.apdex >= 0.85 && h.errorRate <= 0.01);
@@ -509,6 +522,7 @@ function generateFinalAgentReport(history) {
509
522
  console.log("\n=======================================================");
510
523
  console.log("\u{1F9E0} INTELLIGENT AUTONOMOUS LOAD AGENT FINAL REPORT");
511
524
  console.log("=======================================================");
525
+ console.log(`\u{1F49A} Unified Blaze Health Score : ${score}/100`);
512
526
  console.log(`\u{1F3C6} Recommended Max Safe Concurrency : ${maxSafeThreads} allocation threads`);
513
527
  console.log(`\u{1F4A5} Infrastructure Failure Boundary : ${history[history.length - 1].threads} concurrency allocation`);
514
528
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
@@ -536,7 +550,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
536
550
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
537
551
  const uniquePatternsCount = semanticReport.clusters.length;
538
552
  logClustersHtml = `
539
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
553
+ <div class="card p-dev" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
540
554
  <h3 style="border-bottom: none; padding-bottom: 0rem; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; color: #ffffff; font-size: 1.2rem;">
541
555
  \u{1F9E0} Semantic Log Clustering & Error Classification
542
556
  </h3>
@@ -550,7 +564,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
550
564
  <th style="width: 8%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
551
565
  <th style="width: 22%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Classification Category</th>
552
566
  <th style="width: 10%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
553
- <th style="width: 60%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Structural Abstract Blueprint / Dynamic Log Fingerprint</th>
567
+ <th style="width: 60%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Structural Abstract Blueprint</th>
554
568
  </tr>
555
569
  </thead>
556
570
  <tbody>
@@ -558,7 +572,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
558
572
  let catColor = "#fb923c";
559
573
  if (c.category === "Authentication_Error") catColor = "#c084fc";
560
574
  if (c.category === "Product_Error") catColor = "#f87171";
561
- if (c.category === "Environment_Infrastructure_Error") catColor = "#fb923c";
562
575
  let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
563
576
  const rawTemplate = c.template || "";
564
577
  const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
@@ -573,9 +586,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
573
586
  <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-left: 3px solid #1e293b; margin-bottom: 0.5rem;">
574
587
  <code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
575
588
  </div>
576
- <div style="color: #64748b; font-size: 0.8rem; font-family: monospace; padding-left: 0.2rem; line-height: 1.4;">
577
- <span style="color: #475569;">Real Example Trace:</span> ${rawTemplate}
578
- </div>
579
589
  </td>
580
590
  </tr>`;
581
591
  }).join("")}
@@ -588,25 +598,20 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
588
598
  const finalLatency = history[history.length - 1]?.avgLatency || cards.ttfb;
589
599
  const seoMetrics = calculateSeoImpactMetrics(finalLatency);
590
600
  seoImpactSectionHtml = `
591
- <div class="card" style="margin-top: 2rem; background: #111a2e; border: 1px solid #1e3a8a;">
601
+ <div class="card p-exec" style="margin-top: 2rem; background: #111a2e; border: 1px solid #1e3a8a;">
592
602
  <h3 style="color: #f43f5e; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
593
603
  \u{1F50E} SEO Rank Impact & Visibility Loss Predictor
594
604
  </h3>
595
- <p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
596
- Translates response time infrastructure load stress into Core Web Vitals ranking drops and organic search volume degradation metrics.
597
- </p>
598
605
  <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
599
606
  <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
600
607
  <div class="card-title" style="color: #94a3b8;">Est. Organic Traffic Loss</div>
601
608
  <div style="font-size: 2rem; font-weight: bold; color: ${seoMetrics.color};">
602
609
  ${seoMetrics.trafficLoss > 0 ? "-" : ""}${seoMetrics.trafficLoss}%
603
610
  </div>
604
- <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">compared to 200ms sweet-spot</div>
605
611
  </div>
606
612
  <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
607
613
  <div class="card-title">Search Visibility Status</div>
608
614
  <div style="font-size: 1.25rem; font-weight: bold; color: ${seoMetrics.color}; margin-top: 0.4rem;">${seoMetrics.status}</div>
609
- <div style="font-size: 0.8rem; color: #94a3b8; margin-top: 0.25rem;">Target Latency: ${finalLatency.toFixed(1)}ms</div>
610
615
  </div>
611
616
  <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
612
617
  <div class="card-title">Googlebot Crawl Budget Impact</div>
@@ -616,7 +621,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
616
621
  </div>`;
617
622
  }
618
623
  const liveAdjusterHtml = `
619
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
624
+ <div class="card p-devops" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
620
625
  <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 1rem;">
621
626
  \u{1F39B}\uFE0F Live Concurrency Adjuster (Mid-Test Simulation)
622
627
  </h3>
@@ -632,15 +637,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
632
637
  <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Throughput</div>
633
638
  <div id="simThroughput" style="font-size: 1.2rem; font-weight: bold; color: #f97316;">${cards.throughput.toFixed(0)}/s</div>
634
639
  </div>
635
- <div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
636
- <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Latency</div>
637
- <div id="simLatency" style="font-size: 1.2rem; font-weight: bold; color: #10b981;">${cards.ttfb.toFixed(1)}ms</div>
638
- </div>
639
640
  </div>
640
641
  </div>
641
642
  </div>`;
642
643
  const rightSizingHtml = `
643
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
644
+ <div class="card p-devops p-exec" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
644
645
  <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem;">
645
646
  \u2601\uFE0F Infrastructure Right-Sizing & Cloud Cost Projections
646
647
  </h3>
@@ -670,6 +671,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
670
671
  </div>
671
672
  `;
672
673
  }).join("");
674
+ const gaugeColor = cards.healthScore >= 90 ? "#10b981" : cards.healthScore >= 70 ? "#f59e0b" : "#ef4444";
673
675
  const htmlContent = `<!DOCTYPE html><html lang="en"><head>
674
676
  <meta charset="UTF-8">
675
677
  <title>Blaze Core Performance Report</title>
@@ -677,10 +679,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
677
679
  <style>
678
680
  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
679
681
  .container { max-width: 1300px; margin: 0 auto; }
680
- .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
682
+ .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; display: flex; justify-content: space-between; align-items: center; }
681
683
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
682
684
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
683
685
 
686
+ /* Persona Controls Styling */
687
+ .persona-toggle-container { background: #131c2e; padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid #1e293b; display: flex; align-items: center; gap: 0.75rem; }
688
+ .persona-btn { background: #090d16; border: 1px solid #1e293b; color: #94a3b8; padding: 0.4rem 1rem; font-size: 0.85rem; font-weight: 600; border-radius: 6px; cursor: pointer; transition: all 0.2s ease; }
689
+ .persona-btn.active { background: #38bdf8; border-color: #38bdf8; color: #0b111e; }
690
+
684
691
  .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
685
692
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
686
693
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
@@ -691,13 +698,16 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
691
698
  .card-value.purple { color: #a855f7; }
692
699
  .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
693
700
 
694
- .top-layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.5rem; margin-bottom: 2rem; }
701
+ .top-layout-grid { display: grid; grid-template-columns: 1fr 280px 380px; gap: 1.5rem; margin-bottom: 2rem; }
695
702
  .verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
696
703
  .verdict-title { text-transform: uppercase; font-size: 0.85rem; font-weight: 700; color: #f97316; margin-bottom: 0.5rem; }
697
704
  .verdict-text { font-size: 0.95rem; line-height: 1.5; color: #e2e8f0; margin-bottom: 0.75rem; }
698
705
  .verdict-waves { background: #1f2937; border-radius: 6px; padding: 0.75rem 1rem; font-family: monospace; color: #9ca3af; font-size: 0.9rem; }
699
706
  .wave-item { margin: 0.25rem 0; }
700
707
 
708
+ .gauge-container-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
709
+ .gauge-title { font-size: 0.85rem; font-weight: bold; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 1rem; }
710
+
701
711
  .matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
702
712
  .matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
703
713
  .matrix-box .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
@@ -726,66 +736,84 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
726
736
  </style></head><body>
727
737
  <div class="container">
728
738
  <div class="header">
729
- <h1>\u{1F525} Blaze Core Performance Analysis</h1>
730
- <div class="meta">Target Script: <code>${config.targetScript}</code></div>
739
+ <div>
740
+ <h1>\u{1F525} Blaze Core Performance Analysis</h1>
741
+ <div class="meta">Target Script: <code>${config.targetScript}</code></div>
742
+ </div>
743
+
744
+ <!-- IMPLEMENTED: Persona Selector Toggle Buttons -->
745
+ <div class="persona-toggle-container">
746
+ <span style="font-size: 0.8rem; color: #64748b; font-weight: bold; text-transform: uppercase;">View Perspective:</span>
747
+ <button class="persona-btn active" onclick="switchPersona('developer')">Developer</button>
748
+ <button class="persona-btn" onclick="switchPersona('devops')">DevOps</button>
749
+ <button class="persona-btn" onclick="switchPersona('executive')">Executive</button>
750
+ </div>
731
751
  </div>
732
752
 
733
753
  <div class="cards-wrapper">
734
- <div class="metric-card">
754
+ <div class="metric-card p-dev">
735
755
  <div class="card-title">Apdex Score (T: 50ms)</div>
736
756
  <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">(Good)</span></div>
737
757
  </div>
738
- <div class="metric-card">
758
+ <div class="metric-card p-devops p-exec">
739
759
  <div class="card-title">Throughput</div>
740
760
  <div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
741
761
  </div>
742
- <div class="metric-card">
762
+ <div class="metric-card p-devops">
743
763
  <div class="card-title">Network Bandwidth</div>
744
764
  <div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
745
765
  </div>
746
- <div class="metric-card">
766
+ <div class="metric-card p-devops">
747
767
  <div class="card-title">Avg Time To First Byte</div>
748
768
  <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
749
769
  </div>
750
- <div class="metric-card">
770
+ <div class="metric-card p-dev">
751
771
  <div class="card-title">DNS Lookup</div>
752
772
  <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
753
773
  </div>
754
- <div class="metric-card">
774
+ <div class="metric-card p-dev">
755
775
  <div class="card-title">TCP Connect</div>
756
776
  <div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
757
777
  </div>
758
- <div class="metric-card">
778
+ <div class="metric-card p-dev">
759
779
  <div class="card-title">TLS Handshake</div>
760
780
  <div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
761
781
  </div>
762
- <div class="metric-card">
782
+ <div class="metric-card p-dev p-exec">
763
783
  <div class="card-title">Stability (Std Dev)</div>
764
784
  <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
765
785
  </div>
766
786
  </div>
767
787
 
768
788
  <div class="top-layout-grid">
769
- <div class="verdict-box">
789
+ <div class="verdict-box p-devops p-exec">
770
790
  <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
771
791
  <div class="verdict-text">${verdictReason}</div>
772
792
  <div class="verdict-waves">${waveListItems}</div>
773
793
  </div>
774
794
 
775
- <div class="matrix-box">
776
- <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
777
- <div class="matrix-row">
778
- <div class="matrix-label">Informational <span class="matrix-badge badge-1xx">1xx</span></div>
779
- <div class="matrix-value">${responseMatrix.total1xx}</div>
795
+ <div class="gauge-container-box p-exec p-devops">
796
+ <div class="gauge-title">Blaze Run Health Score</div>
797
+ <div style="position: relative; width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;">
798
+ <svg width="140" height="140" viewBox="0 0 140 140" style="transform: rotate(-90deg);">
799
+ <circle cx="70" cy="70" r="58" stroke="#1e293b" stroke-width="12" fill="transparent"/>
800
+ <circle cx="70" cy="70" r="58" stroke="${gaugeColor}" stroke-width="12" fill="transparent"
801
+ stroke-dasharray="364.4" stroke-dashoffset="${364.4 - 364.4 * cards.healthScore / 100}"
802
+ stroke-linecap="round"/>
803
+ </svg>
804
+ <div style="position: absolute; text-align: center;">
805
+ <div style="font-size: 2.2rem; font-weight: 800; color: #ffffff; line-height: 1;">${cards.healthScore}</div>
806
+ <div style="font-size: 0.75rem; color: #64748b; font-weight: bold; margin-top: 0.2rem; text-transform: uppercase;">/ 100</div>
807
+ </div>
780
808
  </div>
809
+ </div>
810
+
811
+ <div class="matrix-box p-devops p-dev">
812
+ <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
781
813
  <div class="matrix-row">
782
814
  <div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
783
815
  <div class="matrix-value">${responseMatrix.total2xx}</div>
784
816
  </div>
785
- <div class="matrix-row">
786
- <div class="matrix-label">Redirects <span class="matrix-badge badge-3xx">3xx</span></div>
787
- <div class="matrix-value">${responseMatrix.total3xx}</div>
788
- </div>
789
817
  <div class="matrix-row">
790
818
  <div class="matrix-label">Client Error <span class="matrix-badge badge-4xx">4xx</span></div>
791
819
  <div class="matrix-value">${responseMatrix.total4xx}</div>
@@ -795,7 +823,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
795
823
  <div class="matrix-value">${responseMatrix.total5xx}</div>
796
824
  </div>
797
825
 
798
- <!-- NEW SUBSECTION: Individual HTTP Status Code Breakdown Rates -->
799
826
  <div class="matrix-title" style="margin-top: 1.5rem; font-size: 0.85rem; border-top: 1px solid #1e293b; padding-top: 1rem; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">
800
827
  \u{1F522} HTTP Status Code Breakdown Rate
801
828
  </div>
@@ -806,28 +833,28 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
806
833
  </div>
807
834
 
808
835
  <div class="charts-grid">
809
- <div class="graph-card">
836
+ <div class="graph-card p-devops">
810
837
  <div class="graph-title">\u{1F4CA} Throughput Velocity & Workload Volume</div>
811
838
  <div style="height: 280px; position: relative;">
812
839
  <canvas id="volumeChart"></canvas>
813
840
  </div>
814
841
  </div>
815
842
 
816
- <div class="graph-card">
843
+ <div class="graph-card p-devops">
817
844
  <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend</div>
818
845
  <div style="height: 280px; position: relative;">
819
846
  <canvas id="concurrencyChart"></canvas>
820
847
  </div>
821
848
  </div>
822
849
 
823
- <div class="graph-card">
850
+ <div class="graph-card p-dev">
824
851
  <div class="graph-title">\u{1F369} HTTP Status Code Proportions</div>
825
852
  <div style="height: 280px; position: relative;">
826
853
  <canvas id="statusDonutChart"></canvas>
827
854
  </div>
828
855
  </div>
829
856
 
830
- <div class="graph-card">
857
+ <div class="graph-card p-dev">
831
858
  <div class="graph-title">\u{1F4C8} Time-to-First-Byte Scatter Plot</div>
832
859
  <div style="height: 280px; position: relative;">
833
860
  <canvas id="ttfbScatterChart"></canvas>
@@ -835,7 +862,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
835
862
  </div>
836
863
  </div>
837
864
 
838
- <div class="card">
865
+ <div class="card p-dev">
839
866
  <h3>Telemetry Matrix Logs</h3>
840
867
  <table>
841
868
  <thead>
@@ -844,7 +871,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
844
871
  <th>Throughput</th>
845
872
  <th>Avg Latency</th>
846
873
  <th>P95 Latency</th>
847
- <th>Error Rate</th>
848
874
  <th>Status</th>
849
875
  </tr>
850
876
  </thead>
@@ -856,7 +882,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
856
882
  <td>${h.throughput.toFixed(0)} req/sec</td>
857
883
  <td>${h.avgLatency.toFixed(1)} ms</td>
858
884
  <td>${h.p95Latency.toFixed(1)} ms</td>
859
- <td>${(h.errorRate * 100).toFixed(2)}%</td>
860
885
  <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
861
886
  </tr>`;
862
887
  }).join("")}
@@ -872,56 +897,49 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
872
897
  <script>
873
898
  const labels = ${JSON.stringify(labels)};
874
899
  const baseThroughput = ${cards.throughput};
875
- const baseLatency = ${cards.ttfb};
876
900
 
877
- // Live Concurrency Adjuster interactive client logic
878
901
  const slider = document.getElementById('concurrencySlider');
879
902
  const sliderVal = document.getElementById('sliderValue');
880
903
  const simThroughput = document.getElementById('simThroughput');
881
- const simLatency = document.getElementById('simLatency');
882
904
 
883
- slider.addEventListener('input', (e) => {
884
- const val = parseInt(e.target.value, 10);
885
- sliderVal.textContent = val;
886
- const factor = val / ${config.threads || 10};
887
- simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
888
- simLatency.textContent = (baseLatency * Math.pow(factor, 0.8)).toFixed(1) + 'ms';
889
- });
905
+ if(slider) {
906
+ slider.addEventListener('input', (e) => {
907
+ const val = parseInt(e.target.value, 10);
908
+ sliderVal.textContent = val;
909
+ const factor = val / ${config.threads || 10};
910
+ simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
911
+ });
912
+ }
913
+
914
+ // Dynamic View Selection Switching Functionality
915
+ function switchPersona(persona) {
916
+ document.querySelectorAll('.persona-btn').forEach(btn => btn.classList.remove('active'));
917
+ event.target.classList.add('active');
918
+
919
+ // Hide all dynamic components initially
920
+ const layouts = ['.p-dev', '.p-devops', '.p-exec'];
921
+ layouts.forEach(selector => {
922
+ document.querySelectorAll(selector).forEach(el => el.style.display = 'none');
923
+ });
924
+
925
+ // Show specific class tags tied to selected viewport context
926
+ document.querySelectorAll('.p-' + persona).forEach(el => {
927
+ if (el.tagName === 'TR') el.style.display = 'table-row';
928
+ else if (el.classList.contains('metric-card')) el.style.display = 'block';
929
+ else el.style.display = '';
930
+ });
931
+ }
890
932
 
891
933
  new Chart(document.getElementById('volumeChart'), {
892
934
  type: 'bar',
893
935
  data: {
894
936
  labels: labels,
895
937
  datasets: [
896
- {
897
- label: 'Successful Requests',
898
- data: ${JSON.stringify(successRequestsData)},
899
- backgroundColor: '#10b981',
900
- stack: 'requests',
901
- barPercentage: 0.95,
902
- categoryPercentage: 0.95
903
- },
904
- {
905
- label: 'Failed Workloads',
906
- data: ${JSON.stringify(failedWorkloadsData)},
907
- backgroundColor: '#ef4444',
908
- stack: 'requests',
909
- barPercentage: 0.95,
910
- categoryPercentage: 0.95
911
- }
938
+ { label: 'Successful Requests', data: ${JSON.stringify(successRequestsData)}, backgroundColor: '#10b981', stack: 'requests' },
939
+ { label: 'Failed Workloads', data: ${JSON.stringify(failedWorkloadsData)}, backgroundColor: '#ef4444', stack: 'requests' }
912
940
  ]
913
941
  },
914
- options: {
915
- responsive: true,
916
- maintainAspectRatio: false,
917
- plugins: {
918
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
919
- },
920
- scales: {
921
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
922
- y: { stacked: true, grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
923
- }
924
- }
942
+ options: { responsive: true, maintainAspectRatio: false, scales: { x: { grid: { color: '#1e293b' } }, y: { stacked: true, grid: { color: '#1e293b' } } } }
925
943
  });
926
944
 
927
945
  new Chart(document.getElementById('concurrencyChart'), {
@@ -929,86 +947,31 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
929
947
  data: {
930
948
  labels: labels,
931
949
  datasets: [
932
- {
933
- label: 'Active VU Threads',
934
- data: ${JSON.stringify(activeThreadsData)},
935
- borderColor: '#38bdf8',
936
- backgroundColor: 'rgba(56, 189, 248, 0.1)',
937
- borderWidth: 2.5,
938
- pointRadius: 4,
939
- fill: true,
940
- yAxisID: 'yThreads'
941
- },
942
- {
943
- label: 'Time-to-Failure (TTF) Trend',
944
- data: ${JSON.stringify(ttfTrendData)},
945
- borderColor: '#f43f5e',
946
- borderWidth: 2,
947
- borderDash: [5, 5],
948
- pointRadius: 3,
949
- fill: false,
950
- yAxisID: 'yTtf'
951
- }
950
+ { label: 'Active VU Threads', data: ${JSON.stringify(activeThreadsData)}, borderColor: '#38bdf8', yAxisID: 'yThreads' },
951
+ { label: 'Time-to-Failure Trend', data: ${JSON.stringify(ttfTrendData)}, borderColor: '#f43f5e', yAxisID: 'yTtf' }
952
952
  ]
953
953
  },
954
- options: {
955
- responsive: true,
956
- maintainAspectRatio: false,
957
- plugins: {
958
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
959
- },
960
- scales: {
961
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
962
- y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
963
- y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
964
- }
965
- }
954
+ options: { responsive: true, maintainAspectRatio: false }
966
955
  });
967
956
 
968
957
  new Chart(document.getElementById('statusDonutChart'), {
969
958
  type: 'doughnut',
970
959
  data: {
971
- labels: ['1xx Info', '2xx Success', '3xx Redirect', '4xx Client Err', '5xx Server Err'],
972
- datasets: [{
973
- data: [${responseMatrix.total1xx}, ${responseMatrix.total2xx}, ${responseMatrix.total3xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}],
974
- backgroundColor: ['#38bdf8', '#4ade80', '#cbd5e1', '#fde047', '#f87171'],
975
- borderWidth: 1,
976
- borderColor: '#1e293b'
977
- }]
960
+ labels: ['Success', 'Client Err', 'Server Err'],
961
+ datasets: [{ data: [${responseMatrix.total2xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}], backgroundColor: ['#4ade80', '#fde047', '#f87171'] }]
978
962
  },
979
- options: {
980
- responsive: true,
981
- maintainAspectRatio: false,
982
- plugins: {
983
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
984
- },
985
- cutout: '65%'
986
- }
963
+ options: { responsive: true, maintainAspectRatio: false }
987
964
  });
988
965
 
989
966
  new Chart(document.getElementById('ttfbScatterChart'), {
990
967
  type: 'scatter',
991
- data: {
992
- datasets: [{
993
- label: 'Request Processing Delay',
994
- data: ${JSON.stringify(scatterPoints)},
995
- backgroundColor: '#a855f7',
996
- pointRadius: 4,
997
- pointHoverRadius: 6
998
- }]
999
- },
1000
- options: {
1001
- responsive: true,
1002
- maintainAspectRatio: false,
1003
- plugins: {
1004
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
1005
- },
1006
- scales: {
1007
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'Time offset (s)', color: '#64748b' } },
1008
- y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'TTFB / Delay (ms)', color: '#64748b' } }
1009
- }
1010
- }
968
+ data: { datasets: [{ label: 'Processing Delay', data: ${JSON.stringify(scatterPoints)}, backgroundColor: '#a855f7' }] },
969
+ options: { responsive: true, maintainAspectRatio: false }
1011
970
  });
971
+
972
+ // Initialize layout setup to display Developer view on start
973
+ document.querySelectorAll('.p-devops, .p-exec').forEach(el => el.style.display = 'none');
974
+ document.querySelectorAll('.p-dev').forEach(el => el.style.display = '');
1012
975
  </script></body></html>`;
1013
976
  try {
1014
977
  fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.1.21",
3
+ "version": "3.1.23",
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",