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 +225 -70
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
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;
|
|
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;
|
|
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
|
-
<
|
|
735
|
-
|
|
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
|
-
|
|
801
|
-
<div class="matrix-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
<div class="matrix-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
<div class="matrix-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
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
|
-
{
|
|
911
|
-
|
|
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: {
|
|
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:
|
|
1005
|
+
labels: extendedLabels,
|
|
921
1006
|
datasets: [
|
|
922
|
-
{
|
|
923
|
-
|
|
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: {
|
|
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: [{
|
|
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: {
|
|
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: {
|
|
941
|
-
|
|
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
|