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 +122 -159
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
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:
|
|
332
|
-
throughput:
|
|
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 ||
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
<
|
|
730
|
-
|
|
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">±${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="
|
|
776
|
-
<div class="
|
|
777
|
-
<div
|
|
778
|
-
<
|
|
779
|
-
|
|
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
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: ['
|
|
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
|
-
|
|
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
|