blaze-performance-tester 3.1.26 → 3.1.28

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
@@ -540,26 +540,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
540
540
  const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
541
541
  const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
542
542
  const recommendedRamGb = recommendedCpuCores * 2;
543
- const rightSizingHtml = `
544
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
545
- <h3 style="color: #a855f7; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 1rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
546
- \u{1F5A5}\uFE0F Infrastructure Right-Sizing & Cloud Cost Optimization
547
- </h3>
548
- <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
549
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
550
- <div class="card-title" style="color: #94a3b8;">Recommended Compute</div>
551
- <div style="font-size: 1.75rem; font-weight: bold; color: #a855f7; margin-top: 0.4rem;">${recommendedCpuCores} Cores</div>
552
- </div>
553
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
554
- <div class="card-title" style="color: #94a3b8;">Recommended Memory</div>
555
- <div style="font-size: 1.75rem; font-weight: bold; color: #a855f7; margin-top: 0.4rem;">${recommendedRamGb} GB RAM</div>
556
- </div>
557
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
558
- <div class="card-title" style="color: #94a3b8;">Est. Scale Cost / Month</div>
559
- <div style="font-size: 1.75rem; font-weight: bold; color: #10b981; margin-top: 0.4rem;">$${estimatedMonthlyCost}</div>
560
- </div>
561
- </div>
562
- </div>`;
563
543
  const firstTimestamp = rawRequests[0]?.timestampMs || Date.now();
564
544
  const scatterPoints = rawRequests.slice(0, 180).map((r) => ({
565
545
  x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
@@ -584,7 +564,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
584
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>
585
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>
586
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>
587
- <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>
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 / Dynamic Log Fingerprint</th>
588
568
  </tr>
589
569
  </thead>
590
570
  <tbody>
@@ -592,6 +572,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
592
572
  let catColor = "#fb923c";
593
573
  if (c.category === "Authentication_Error") catColor = "#c084fc";
594
574
  if (c.category === "Product_Error") catColor = "#f87171";
575
+ if (c.category === "Environment_Infrastructure_Error") catColor = "#fb923c";
595
576
  let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
596
577
  const rawTemplate = c.template || "";
597
578
  const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
@@ -606,6 +587,9 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
606
587
  <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-left: 3px solid #1e293b; margin-bottom: 0.5rem;">
607
588
  <code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
608
589
  </div>
590
+ <div style="color: #64748b; font-size: 0.8rem; font-family: monospace; padding-left: 0.2rem; line-height: 1.4;">
591
+ <span style="color: #475569;">Real Example Trace:</span> ${rawTemplate}
592
+ </div>
609
593
  </td>
610
594
  </tr>`;
611
595
  }).join("")}
@@ -622,16 +606,21 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
622
606
  <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;">
623
607
  \u{1F50E} SEO Rank Impact & Visibility Loss Predictor
624
608
  </h3>
609
+ <p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
610
+ Translates response time infrastructure load stress into Core Web Vitals ranking drops and organic search volume degradation metrics.
611
+ </p>
625
612
  <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
626
613
  <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
627
614
  <div class="card-title" style="color: #94a3b8;">Est. Organic Traffic Loss</div>
628
615
  <div style="font-size: 2rem; font-weight: bold; color: ${seoMetrics.color};">
629
616
  ${seoMetrics.trafficLoss > 0 ? "-" : ""}${seoMetrics.trafficLoss}%
630
617
  </div>
618
+ <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">compared to 200ms sweet-spot</div>
631
619
  </div>
632
620
  <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
633
621
  <div class="card-title">Search Visibility Status</div>
634
622
  <div style="font-size: 1.25rem; font-weight: bold; color: ${seoMetrics.color}; margin-top: 0.4rem;">${seoMetrics.status}</div>
623
+ <div style="font-size: 0.8rem; color: #94a3b8; margin-top: 0.25rem;">Target Latency: ${finalLatency.toFixed(1)}ms</div>
635
624
  </div>
636
625
  <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
637
626
  <div class="card-title">Googlebot Crawl Budget Impact</div>
@@ -657,6 +646,30 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
657
646
  <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Throughput</div>
658
647
  <div id="simThroughput" style="font-size: 1.2rem; font-weight: bold; color: #f97316;">${cards.throughput.toFixed(0)}/s</div>
659
648
  </div>
649
+ <div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
650
+ <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Latency</div>
651
+ <div id="simLatency" style="font-size: 1.2rem; font-weight: bold; color: #10b981;">${cards.ttfb.toFixed(1)}ms</div>
652
+ </div>
653
+ </div>
654
+ </div>
655
+ </div>`;
656
+ const rightSizingHtml = `
657
+ <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
658
+ <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem;">
659
+ \u2601\uFE0F Infrastructure Right-Sizing & Cloud Cost Projections
660
+ </h3>
661
+ <div style="grid-template-columns: repeat(3, 1fr); display: grid; gap: 1rem; margin-top: 1rem;">
662
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
663
+ <div class="card-title">Est. Monthly Cloud Cost</div>
664
+ <div class="card-value green">$${estimatedMonthlyCost} <span class="unit">/mo</span></div>
665
+ </div>
666
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
667
+ <div class="card-title">Recommended CPU Right-Sizing</div>
668
+ <div class="card-value purple">${recommendedCpuCores} <span class="unit">vCores</span></div>
669
+ </div>
670
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
671
+ <div class="card-title">Recommended Memory Allocation</div>
672
+ <div class="card-value orange">${recommendedRamGb} <span class="unit">GB RAM</span></div>
660
673
  </div>
661
674
  </div>
662
675
  </div>`;
@@ -679,7 +692,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
679
692
  <style>
680
693
  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
681
694
  .container { max-width: 1300px; margin: 0 auto; }
682
- .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; display: flex; justify-content: space-between; align-items: center; }
695
+ .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
683
696
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
684
697
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
685
698
 
@@ -703,7 +716,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
703
716
  .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; }
704
717
  .gauge-title { font-size: 0.85rem; font-weight: bold; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 1rem; }
705
718
 
706
- .matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; display: flex; flex-direction: column; justify-content: space-between; }
719
+ .matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
707
720
  .matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
708
721
  .matrix-box .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
709
722
  .matrix-box .matrix-row:last-child { border-bottom: none; }
@@ -731,10 +744,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
731
744
  </style></head><body>
732
745
  <div class="container">
733
746
  <div class="header">
734
- <div>
735
- <h1>\u{1F525} Blaze Core Performance Analysis</h1>
736
- <div class="meta">Target Script: <code>${config.targetScript}</code></div>
737
- </div>
747
+ <h1>\u{1F525} Blaze Core Performance Analysis</h1>
748
+ <div class="meta">Target Script: <code>${config.targetScript}</code></div>
738
749
  </div>
739
750
 
740
751
  <div class="cards-wrapper">
@@ -786,39 +797,46 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
786
797
  <circle cx="70" cy="70" r="58" stroke="#1e293b" stroke-width="12" fill="transparent"/>
787
798
  <circle cx="70" cy="70" r="58" stroke="${gaugeColor}" stroke-width="12" fill="transparent"
788
799
  stroke-dasharray="364.4" stroke-dashoffset="${364.4 - 364.4 * cards.healthScore / 100}"
789
- stroke-linecap="round"/>
800
+ stroke-linecap="round" style="transition: stroke-dashoffset 1s ease-out;"/>
790
801
  </svg>
791
802
  <div style="position: absolute; text-align: center;">
792
803
  <div style="font-size: 2.2rem; font-weight: 800; color: #ffffff; line-height: 1;">${cards.healthScore}</div>
793
804
  <div style="font-size: 0.75rem; color: #64748b; font-weight: bold; margin-top: 0.2rem; text-transform: uppercase;">/ 100</div>
794
805
  </div>
795
806
  </div>
807
+ <div style="margin-top: 0.75rem; font-size: 0.8rem; color: #94a3b8; font-weight: 500;">
808
+ Composite Run Quality Engine Rating
809
+ </div>
796
810
  </div>
797
811
 
798
812
  <div class="matrix-box">
799
- <div>
800
- <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
801
- <div class="matrix-row">
802
- <div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
803
- <div class="matrix-value">${responseMatrix.total2xx}</div>
804
- </div>
805
- <div class="matrix-row">
806
- <div class="matrix-label">Client Error <span class="matrix-badge badge-4xx">4xx</span></div>
807
- <div class="matrix-value">${responseMatrix.total4xx}</div>
808
- </div>
809
- <div class="matrix-row">
810
- <div class="matrix-label">Server Error <span class="matrix-badge badge-5xx">5xx</span></div>
811
- <div class="matrix-value">${responseMatrix.total5xx}</div>
812
- </div>
813
+ <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
814
+ <div class="matrix-row">
815
+ <div class="matrix-label">Informational <span class="matrix-badge badge-1xx">1xx</span></div>
816
+ <div class="matrix-value">${responseMatrix.total1xx}</div>
817
+ </div>
818
+ <div class="matrix-row">
819
+ <div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
820
+ <div class="matrix-value">${responseMatrix.total2xx}</div>
821
+ </div>
822
+ <div class="matrix-row">
823
+ <div class="matrix-label">Redirects <span class="matrix-badge badge-3xx">3xx</span></div>
824
+ <div class="matrix-value">${responseMatrix.total3xx}</div>
825
+ </div>
826
+ <div class="matrix-row">
827
+ <div class="matrix-label">Client Error <span class="matrix-badge badge-4xx">4xx</span></div>
828
+ <div class="matrix-value">${responseMatrix.total4xx}</div>
829
+ </div>
830
+ <div class="matrix-row">
831
+ <div class="matrix-label">Server Error <span class="matrix-badge badge-5xx">5xx</span></div>
832
+ <div class="matrix-value">${responseMatrix.total5xx}</div>
813
833
  </div>
814
834
 
815
- <div>
816
- <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;">
817
- \u{1F522} HTTP Status Code Breakdown Rate
818
- </div>
819
- <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-top: 0.5rem;">
820
- ${breakdownGridHtml}
821
- </div>
835
+ <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;">
836
+ \u{1F522} HTTP Status Code Breakdown Rate
837
+ </div>
838
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-top: 0.5rem;">
839
+ ${breakdownGridHtml}
822
840
  </div>
823
841
  </div>
824
842
  </div>
@@ -832,7 +850,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
832
850
  </div>
833
851
 
834
852
  <div class="graph-card">
835
- <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend</div>
853
+ <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend & AI Load Forecast</div>
836
854
  <div style="height: 280px; position: relative;">
837
855
  <canvas id="concurrencyChart"></canvas>
838
856
  </div>
@@ -862,6 +880,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
862
880
  <th>Throughput</th>
863
881
  <th>Avg Latency</th>
864
882
  <th>P95 Latency</th>
883
+ <th>Error Rate</th>
865
884
  <th>Status</th>
866
885
  </tr>
867
886
  </thead>
@@ -873,6 +892,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
873
892
  <td>${h.throughput.toFixed(0)} req/sec</td>
874
893
  <td>${h.avgLatency.toFixed(1)} ms</td>
875
894
  <td>${h.p95Latency.toFixed(1)} ms</td>
895
+ <td>${(h.errorRate * 100).toFixed(2)}%</td>
876
896
  <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
877
897
  </tr>`;
878
898
  }).join("")}
@@ -888,57 +908,192 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
888
908
  <script>
889
909
  const labels = ${JSON.stringify(labels)};
890
910
  const baseThroughput = ${cards.throughput};
911
+ const baseLatency = ${cards.ttfb};
891
912
 
892
913
  const slider = document.getElementById('concurrencySlider');
893
914
  const sliderVal = document.getElementById('sliderValue');
894
915
  const simThroughput = document.getElementById('simThroughput');
916
+ const simLatency = document.getElementById('simLatency');
895
917
 
896
- if(slider) {
897
- slider.addEventListener('input', (e) => {
898
- const val = parseInt(e.target.value, 10);
899
- sliderVal.textContent = val;
900
- const factor = val / ${config.threads || 10};
901
- simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
902
- });
903
- }
918
+ slider.addEventListener('input', (e) => {
919
+ const val = parseInt(e.target.value, 10);
920
+ sliderVal.textContent = val;
921
+ const factor = val / ${config.threads || 10};
922
+ simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
923
+ simLatency.textContent = (baseLatency * Math.pow(factor, 0.8)).toFixed(1) + 'ms';
924
+ });
904
925
 
905
926
  new Chart(document.getElementById('volumeChart'), {
906
927
  type: 'bar',
907
928
  data: {
908
929
  labels: labels,
909
930
  datasets: [
910
- { label: 'Successful Requests', data: ${JSON.stringify(successRequestsData)}, backgroundColor: '#10b981', stack: 'requests' },
911
- { label: 'Failed Workloads', data: ${JSON.stringify(failedWorkloadsData)}, backgroundColor: '#ef4444', stack: 'requests' }
931
+ {
932
+ label: 'Successful Requests',
933
+ data: ${JSON.stringify(successRequestsData)},
934
+ backgroundColor: '#10b981',
935
+ stack: 'requests',
936
+ barPercentage: 0.95,
937
+ categoryPercentage: 0.95
938
+ },
939
+ {
940
+ label: 'Failed Workloads',
941
+ data: ${JSON.stringify(failedWorkloadsData)},
942
+ backgroundColor: '#ef4444',
943
+ stack: 'requests',
944
+ barPercentage: 0.95,
945
+ categoryPercentage: 0.95
946
+ }
912
947
  ]
913
948
  },
914
- options: { responsive: true, maintainAspectRatio: false, scales: { x: { grid: { color: '#1e293b' } }, y: { stacked: true, grid: { color: '#1e293b' } } } }
949
+ options: {
950
+ responsive: true,
951
+ maintainAspectRatio: false,
952
+ plugins: {
953
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
954
+ },
955
+ scales: {
956
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
957
+ y: { stacked: true, grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
958
+ }
959
+ }
915
960
  });
916
961
 
962
+ // --- AI FORECASTING LINE REGRESSION SETUP ---
963
+ const baseActiveThreads = ${JSON.stringify(activeThreadsData)};
964
+ const baseTtfTrend = ${JSON.stringify(ttfTrendData)};
965
+ const historicalCount = baseTtfTrend.length;
966
+
967
+ let extendedLabels = [...labels];
968
+ let extendedThreads = [...baseActiveThreads];
969
+ let extendedTtf = [...baseTtfTrend];
970
+ let aiForecastData = [];
971
+
972
+ if (historicalCount >= 2) {
973
+ let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
974
+ for (let i = 0; i < historicalCount; i++) {
975
+ sumX += i;
976
+ sumY += baseTtfTrend[i];
977
+ sumXY += i * baseTtfTrend[i];
978
+ sumXX += i * i;
979
+ }
980
+ const denominator = (historicalCount * sumXX - sumX * sumX);
981
+ const slope = denominator === 0 ? 0 : (historicalCount * sumXY - sumX * sumY) / denominator;
982
+ const intercept = (sumY - slope * sumX) / historicalCount;
983
+
984
+ for (let i = 0; i < historicalCount; i++) {
985
+ aiForecastData.push(Number((slope * i + intercept).toFixed(1)));
986
+ }
987
+
988
+ const lastThreads = baseActiveThreads[historicalCount - 1] || 10;
989
+ const threadStep = ${config.stepSize || 15};
990
+
991
+ // Project out the next 3 hypothetical execution tiers
992
+ for (let i = 1; i <= 3; i++) {
993
+ extendedLabels.push(\`+\${i * 5}s (AI Forecast)\`);
994
+ extendedThreads.push(lastThreads + (threadStep * i));
995
+ extendedTtf.push(null);
996
+ aiForecastData.push(Number((slope * (historicalCount + i - 1) + intercept).toFixed(1)));
997
+ }
998
+ } else {
999
+ aiForecastData = [...baseTtfTrend];
1000
+ }
1001
+
917
1002
  new Chart(document.getElementById('concurrencyChart'), {
918
1003
  type: 'line',
919
1004
  data: {
920
- labels: labels,
1005
+ labels: extendedLabels,
921
1006
  datasets: [
922
- { label: 'Active VU Threads', data: ${JSON.stringify(activeThreadsData)}, borderColor: '#38bdf8', yAxisID: 'yThreads' },
923
- { label: 'Time-to-Failure Trend', data: ${JSON.stringify(ttfTrendData)}, borderColor: '#f43f5e', yAxisID: 'yTtf' }
1007
+ {
1008
+ label: 'Active VU Threads',
1009
+ data: extendedThreads,
1010
+ borderColor: '#38bdf8',
1011
+ backgroundColor: 'rgba(56, 189, 248, 0.1)',
1012
+ borderWidth: 2.5,
1013
+ pointRadius: 4,
1014
+ fill: true,
1015
+ yAxisID: 'yThreads'
1016
+ },
1017
+ {
1018
+ label: 'Time-to-Failure (TTF) Trend',
1019
+ data: extendedTtf,
1020
+ borderColor: '#f43f5e',
1021
+ borderWidth: 2,
1022
+ borderDash: [5, 5],
1023
+ pointRadius: 3,
1024
+ fill: false,
1025
+ yAxisID: 'yTtf'
1026
+ },
1027
+ {
1028
+ label: '\u{1F52E} AI Capacity Forecast Line',
1029
+ data: aiForecastData,
1030
+ borderColor: '#a855f7',
1031
+ borderWidth: 2,
1032
+ borderDash: [3, 3],
1033
+ pointRadius: 4,
1034
+ pointBackgroundColor: '#a855f7',
1035
+ fill: false,
1036
+ yAxisID: 'yTtf'
1037
+ }
924
1038
  ]
925
1039
  },
926
- options: { responsive: true, maintainAspectRatio: false }
1040
+ options: {
1041
+ responsive: true,
1042
+ maintainAspectRatio: false,
1043
+ plugins: {
1044
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
1045
+ },
1046
+ scales: {
1047
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
1048
+ y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
1049
+ y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
1050
+ }
1051
+ }
927
1052
  });
928
1053
 
929
1054
  new Chart(document.getElementById('statusDonutChart'), {
930
1055
  type: 'doughnut',
931
1056
  data: {
932
- labels: ['Success', 'Client Err', 'Server Err'],
933
- datasets: [{ data: [${responseMatrix.total2xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}], backgroundColor: ['#4ade80', '#fde047', '#f87171'] }]
1057
+ labels: ['1xx Info', '2xx Success', '3xx Redirect', '4xx Client Err', '5xx Server Err'],
1058
+ datasets: [{
1059
+ data: [${responseMatrix.total1xx}, ${responseMatrix.total2xx}, ${responseMatrix.total3xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}],
1060
+ backgroundColor: ['#38bdf8', '#4ade80', '#cbd5e1', '#fde047', '#f87171'],
1061
+ borderWidth: 1,
1062
+ borderColor: '#1e293b'
1063
+ }]
934
1064
  },
935
- options: { responsive: true, maintainAspectRatio: false }
1065
+ options: {
1066
+ responsive: true,
1067
+ maintainAspectRatio: false,
1068
+ plugins: {
1069
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } },
1070
+ cutout: '65%'
1071
+ }
1072
+ }
936
1073
  });
937
1074
 
938
1075
  new Chart(document.getElementById('ttfbScatterChart'), {
939
1076
  type: 'scatter',
940
- data: { datasets: [{ label: 'Processing Delay', data: ${JSON.stringify(scatterPoints)}, backgroundColor: '#a855f7' }] },
941
- options: { responsive: true, maintainAspectRatio: false }
1077
+ data: {
1078
+ datasets: [{
1079
+ label: 'Request Processing Delay',
1080
+ data: ${JSON.stringify(scatterPoints)},
1081
+ backgroundColor: '#a855f7',
1082
+ pointRadius: 4,
1083
+ pointHoverRadius: 6
1084
+ }]
1085
+ },
1086
+ options: {
1087
+ responsive: true,
1088
+ maintainAspectRatio: false,
1089
+ plugins: {
1090
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
1091
+ },
1092
+ scales: {
1093
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'Time offset (s)', color: '#64748b' } },
1094
+ y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'TTFB / Delay (ms)', color: '#64748b' } }
1095
+ }
1096
+ }
942
1097
  });
943
1098
  </script></body></html>`;
944
1099
  try {
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.1.26",
3
+ "version": "3.1.28",
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",