blaze-performance-tester 3.1.7 → 3.1.10

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
@@ -85,7 +85,9 @@ var mathUtils = {
85
85
  const sorted = [...arr].sort((a, b) => a - b);
86
86
  const index = Math.ceil(p / 100 * sorted.length) - 1;
87
87
  return sorted[Math.max(0, index)];
88
- }
88
+ },
89
+ min: (arr) => arr.length === 0 ? 0 : arr.reduce((min, val) => val < min ? val : min, arr[0]),
90
+ max: (arr) => arr.length === 0 ? 0 : arr.reduce((max, val) => val > max ? val : max, arr[0])
89
91
  };
90
92
  async function executeTestPipeline(config) {
91
93
  if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
@@ -224,6 +226,9 @@ async function runIntelligentAgenticStressTest(config) {
224
226
  let lastRawRequests = [];
225
227
  let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
226
228
  let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [], lcp: [], fid: [], cls: [] };
229
+ let totalEconnreset = 0;
230
+ let totalEconnrefused = 0;
231
+ const globalCodeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
227
232
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
228
233
  let baseStep = config.stepSize;
229
234
  let isStable = true;
@@ -241,6 +246,11 @@ async function runIntelligentAgenticStressTest(config) {
241
246
  total3xx += metrics.c3xx;
242
247
  total4xx += metrics.c4xx;
243
248
  total5xx += metrics.c5xx;
249
+ totalEconnreset += metrics.econnresetCount;
250
+ totalEconnrefused += metrics.econnrefusedCount;
251
+ Object.keys(globalCodeCounts).forEach((code) => {
252
+ globalCodeCounts[code] += metrics.codeCounts[code] || 0;
253
+ });
244
254
  globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
245
255
  globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
246
256
  globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
@@ -254,6 +264,8 @@ async function runIntelligentAgenticStressTest(config) {
254
264
  threads: currentThreads,
255
265
  avgLatency: metrics.avgLatencyMs,
256
266
  p95Latency: metrics.p95LatencyMs,
267
+ minLatency: metrics.minLatencyMs,
268
+ maxLatency: metrics.maxLatencyMs,
257
269
  errorRate: metrics.errorRate,
258
270
  apdex: metrics.apdexScore,
259
271
  throughput: metrics.requestsPerSecond,
@@ -261,7 +273,10 @@ async function runIntelligentAgenticStressTest(config) {
261
273
  failedRequests: metrics.c4xx + metrics.c5xx,
262
274
  lcp: metrics.lcpMs,
263
275
  fid: metrics.fidMs,
264
- cls: metrics.clsScore
276
+ cls: metrics.clsScore,
277
+ econnresetRate: metrics.econnresetRate,
278
+ econnrefusedCount: metrics.econnrefusedCount,
279
+ codeRates: metrics.codeRates
265
280
  };
266
281
  printMatrixDashboard(metrics, currentThreads);
267
282
  history.push(currentState);
@@ -305,6 +320,11 @@ async function runIntelligentAgenticStressTest(config) {
305
320
  }
306
321
  const totalTestSeconds = history.length * config.durationSec;
307
322
  const vuRampUpVelocity = history.length > 1 && totalTestSeconds > 0 ? (history[history.length - 1].threads - history[0].threads) / totalTestSeconds : config.threads / config.durationSec;
323
+ const totalGlobalRequests = total1xx + total2xx + total3xx + total4xx + total5xx;
324
+ const aggregatedCodeRates = {};
325
+ Object.keys(globalCodeCounts).forEach((code) => {
326
+ aggregatedCodeRates[code] = totalGlobalRequests === 0 ? 0 : globalCodeCounts[code] / totalGlobalRequests * 100;
327
+ });
308
328
  const finalSummaryCards = {
309
329
  apdex: history[history.length - 1]?.apdex || 0,
310
330
  throughput: history[history.length - 1]?.throughput || 0,
@@ -318,7 +338,12 @@ async function runIntelligentAgenticStressTest(config) {
318
338
  vuRampUpVelocity,
319
339
  lcp: mathUtils.mean(globalMetricsAccumulator.lcp),
320
340
  fid: mathUtils.mean(globalMetricsAccumulator.fid),
321
- cls: mathUtils.mean(globalMetricsAccumulator.cls)
341
+ cls: mathUtils.mean(globalMetricsAccumulator.cls),
342
+ econnresetRate: totalGlobalRequests === 0 ? 0 : totalEconnreset / totalGlobalRequests * 100,
343
+ econnrefusedCount: totalEconnrefused,
344
+ codeRates: aggregatedCodeRates,
345
+ minLatency: history.length === 0 ? 0 : Math.min(...history.map((h) => h.minLatency)),
346
+ maxLatency: history.length === 0 ? 0 : Math.max(...history.map((h) => h.maxLatency))
322
347
  };
323
348
  generateFinalAgentReport(history);
324
349
  exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
@@ -331,6 +356,8 @@ async function runStandardStressTest(config) {
331
356
  threads: config.threads,
332
357
  avgLatency: metrics.avgLatencyMs,
333
358
  p95Latency: metrics.p95LatencyMs,
359
+ minLatency: metrics.minLatencyMs,
360
+ maxLatency: metrics.maxLatencyMs,
334
361
  errorRate: metrics.errorRate,
335
362
  apdex: metrics.apdexScore,
336
363
  throughput: metrics.requestsPerSecond,
@@ -338,7 +365,10 @@ async function runStandardStressTest(config) {
338
365
  failedRequests: metrics.c4xx + metrics.c5xx,
339
366
  lcp: metrics.lcpMs,
340
367
  fid: metrics.fidMs,
341
- cls: metrics.clsScore
368
+ cls: metrics.clsScore,
369
+ econnresetRate: metrics.econnresetRate,
370
+ econnrefusedCount: metrics.econnrefusedCount,
371
+ codeRates: metrics.codeRates
342
372
  }];
343
373
  let semanticReport = null;
344
374
  if (config.clusterLogs) {
@@ -360,7 +390,12 @@ async function runStandardStressTest(config) {
360
390
  vuRampUpVelocity: config.threads / config.durationSec,
361
391
  lcp: metrics.lcpMs,
362
392
  fid: metrics.fidMs,
363
- cls: metrics.clsScore
393
+ cls: metrics.clsScore,
394
+ econnresetRate: metrics.econnresetRate,
395
+ econnrefusedCount: metrics.econnrefusedCount,
396
+ codeRates: metrics.codeRates,
397
+ minLatency: metrics.minLatencyMs,
398
+ maxLatency: metrics.maxLatencyMs
364
399
  };
365
400
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
366
401
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
@@ -392,17 +427,29 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
392
427
  const isBreached = threads > targetThresholdBreak;
393
428
  const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
394
429
  const errorPool = getFallbackClusterLogs();
430
+ const targetFailureCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
395
431
  for (let i = 0; i < totalRequests; i++) {
396
- const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
432
+ const isError = Math.random() < (isBreached ? 0.08 : 5e-3);
397
433
  const baseLatency = (25 + Math.random() * 35) * stressFactor;
398
434
  let errorMessage;
399
435
  let statusCode = 200;
400
436
  if (isError) {
401
- statusCode = Math.random() > 0.3 ? 500 : 404;
402
- errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
437
+ const coin = Math.random();
438
+ if (coin < 0.25) {
439
+ statusCode = 502;
440
+ errorMessage = "Error: ECONNRESET socket hang up abruptly by remote host";
441
+ } else if (coin < 0.5) {
442
+ statusCode = 503;
443
+ errorMessage = "Error: ECONNREFUSED 127.0.0.1:8080 - connection rejected by destination target";
444
+ } else {
445
+ statusCode = targetFailureCodes[Math.floor(Math.random() * targetFailureCodes.length)];
446
+ errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
447
+ }
448
+ } else {
449
+ if (Math.random() < 0.03) statusCode = 302;
403
450
  }
404
451
  data.push({
405
- durationMs: isError ? baseLatency * 0.1 : baseLatency,
452
+ durationMs: isError ? baseLatency * 0.1 + 2 : baseLatency + Math.random() * 20,
406
453
  timestampMs: startTime + Math.random() * (durationSec * 1e3),
407
454
  dnsTimeMs: 1 + Math.random() * 0.9,
408
455
  tcpTimeMs: 4 + Math.random() * 1.5,
@@ -419,18 +466,36 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
419
466
  const avgLatencyMs = mathUtils.mean(durations);
420
467
  const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
421
468
  const p95LatencyMs = mathUtils.percentile(durations, 95);
469
+ const minLatencyMs = mathUtils.min(durations);
470
+ const maxLatencyMs = mathUtils.max(durations);
422
471
  const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
423
472
  const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
424
473
  const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
425
474
  const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
426
475
  let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
476
+ let econnresetCount = 0;
477
+ let econnrefusedCount = 0;
478
+ const targetCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
479
+ const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
427
480
  requests.forEach((r) => {
428
481
  if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
429
482
  else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
430
483
  else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
431
484
  else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
432
485
  else if (r.statusCode >= 500) c5xx++;
486
+ if (r.statusCode in codeCounts) {
487
+ codeCounts[r.statusCode]++;
488
+ }
489
+ if (r.errorMessage) {
490
+ if (r.errorMessage.includes("ECONNRESET")) econnresetCount++;
491
+ if (r.errorMessage.includes("ECONNREFUSED")) econnrefusedCount++;
492
+ }
433
493
  });
494
+ const codeRates = {};
495
+ targetCodes.forEach((code) => {
496
+ codeRates[code] = totalRequests === 0 ? 0 : codeCounts[code] / totalRequests * 100;
497
+ });
498
+ const econnresetRate = totalRequests === 0 ? 0 : econnresetCount / totalRequests * 100;
434
499
  const errorCount = c4xx + c5xx;
435
500
  const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
436
501
  const satisfied = requests.filter((r) => r.durationMs <= apdexT && r.statusCode < 400).length;
@@ -446,6 +511,8 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
446
511
  avgLatencyMs,
447
512
  stdDevMs,
448
513
  p95LatencyMs,
514
+ minLatencyMs,
515
+ maxLatencyMs,
449
516
  errorRate,
450
517
  apdexScore,
451
518
  c1xx,
@@ -460,7 +527,12 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
460
527
  bandwidthMb,
461
528
  lcpMs,
462
529
  fidMs,
463
- clsScore
530
+ clsScore,
531
+ econnresetCount,
532
+ econnresetRate,
533
+ econnrefusedCount,
534
+ codeCounts,
535
+ codeRates
464
536
  };
465
537
  }
466
538
  function printMatrixDashboard(m, threads) {
@@ -470,7 +542,7 @@ function printMatrixDashboard(m, threads) {
470
542
  console.log(`-----------------------------------------------------------------------------------------`);
471
543
  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)} |`);
472
544
  console.log(`-----------------------------------------------------------------------------------------`);
473
- console.log(`| Simulated Web Vitals Equivalent -> LCP: ${m.lcpMs.toFixed(0)}ms | FID: ${m.fidMs.toFixed(1)}ms | CLS: ${m.clsScore.toFixed(3)} |`);
545
+ console.log(`| Network & Floor -> RESET: ${m.econnresetRate.toFixed(1)}% | Min/Max Boundary: ${m.minLatencyMs.toFixed(1)}ms / ${m.maxLatencyMs.toFixed(1)}ms`);
474
546
  console.log(`-----------------------------------------------------------------------------------------`);
475
547
  }
476
548
  function generateFinalAgentReport(history) {
@@ -493,7 +565,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
493
565
  const activeThreadsData = history.map((h) => h.threads);
494
566
  const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
495
567
  const waveListItems = history.map((h) => {
496
- return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%, LCP ${h.lcp.toFixed(0)}ms</div>`;
568
+ return `<div class="wave-item">Wave (${h.threads} VUs): Floor ${h.minLatency.toFixed(1)}ms | Ceil ${h.maxLatency.toFixed(1)}ms | P95 ${h.p95Latency.toFixed(1)}ms</div>`;
497
569
  }).join("");
498
570
  const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
499
571
  const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
@@ -509,6 +581,56 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
509
581
  const lcpStat = getLcpStatus(cards.lcp);
510
582
  const fidStat = getFidStatus(cards.fid);
511
583
  const clsStat = getClsStatus(cards.cls);
584
+ const connectionDiagnosticsPanelHtml = `
585
+ <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
586
+ <h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
587
+ \u{1F50C} Network Connection & Detailed HTTP Status Diagnostics
588
+ </h3>
589
+ <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
590
+ Real-time breakdown tracking transport anomalies and precise layer-7 distribution indicators.
591
+ </div>
592
+
593
+ <div style="display: grid; grid-template-columns: 1fr 2fr; gap: 2rem; flex-wrap: wrap;">
594
+ <div style="display: flex; flex-direction: column; gap: 1rem;">
595
+ <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
596
+ <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNRESET Rate</div>
597
+ <div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnresetRate > 1 ? "#ef4444" : "#10b981"};">
598
+ ${cards.econnresetRate.toFixed(2)}<span style="font-size: 1rem; color: #94a3b8;"> %</span>
599
+ </div>
600
+ <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Abrupt remote host termination frequency</div>
601
+ </div>
602
+
603
+ <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
604
+ <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNREFUSED Count</div>
605
+ <div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnrefusedCount > 0 ? "#f59e0b" : "#10b981"};">
606
+ ${cards.econnrefusedCount}<span style="font-size: 1rem; color: #94a3b8;"> drops</span>
607
+ </div>
608
+ <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Total complete backend port link rejections</div>
609
+ </div>
610
+ </div>
611
+
612
+ <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
613
+ <div style="font-size: 0.85rem; font-weight: bold; color: #f8fafc; margin-bottom: 1rem;">Target Response Code Matrix Rates</div>
614
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
615
+ ${Object.keys(cards.codeRates).map((code) => {
616
+ const percentage = cards.codeRates[code];
617
+ let textColor = "#cbd5e1";
618
+ if (percentage > 0) {
619
+ textColor = code.startsWith("5") ? "#f87171" : "#fde047";
620
+ }
621
+ return `
622
+ <div style="background: #131c2e; padding: 0.75rem; border-radius: 4px; border: 1px solid #1e293b; text-align: center;">
623
+ <div style="font-size: 0.8rem; font-weight: bold; color: #94a3b8;">Code ${code}</div>
624
+ <div style="font-size: 1.1rem; font-weight: bold; margin-top: 0.25rem; color: ${textColor};">
625
+ ${percentage.toFixed(2)}%
626
+ </div>
627
+ </div>
628
+ `;
629
+ }).join("")}
630
+ </div>
631
+ </div>
632
+ </div>
633
+ </div>`;
512
634
  let logClustersHtml = "";
513
635
  if (semanticReport) {
514
636
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
@@ -519,7 +641,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
519
641
  \u{1F9E0} Interactive Remediation Playbooks & Log Classification
520
642
  </h3>
521
643
  <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
522
- Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns. Expand a playbook to inspect automated remediation strategies and diagnostics confidence ratings.
644
+ Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
523
645
  </div>
524
646
 
525
647
  <div style="display: flex; flex-direction: column; gap: 1rem;">
@@ -542,11 +664,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
542
664
  let explanation = "High concurrency is causing socket/connection starvation. Increase connection limits or enable pooling keep-alives.";
543
665
  if (c.category === "Authentication_Error") {
544
666
  fixTitle = "Auth Token / JWT Strategy";
545
- fixCode = "// Implement token caching and silent rotation\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
667
+ fixCode = "// Implement token caching and silent rotation\\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
546
668
  explanation = "Frequent token verification failures under load. Extend signature verification cache or decouple auth validation check.";
547
669
  } else if (c.category === "Product_Error") {
548
670
  fixTitle = "Application Null-Safety / Guard Fix";
549
- fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\nif (!configId) throw new ValidationError('Missing config_id');";
671
+ fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\\nif (!configId) throw new ValidationError('Missing config_id');";
550
672
  explanation = "Runtime reference error for undefined payload field under race condition spikes. Add optional chaining and input contracts.";
551
673
  }
552
674
  const snippetId = `snippet-${idx}`;
@@ -636,7 +758,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
636
758
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
637
759
 
638
760
  .section-title { font-size: 1.2rem; color: #cbd5e1; font-weight: bold; margin: 2rem 0 1rem 0; padding-bottom: 0.4rem; border-bottom: 1px solid #1e293b; }
639
- .cards-wrapper { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
761
+ .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
762
+ @media (max-width: 1024px) { .cards-wrapper { grid-template-columns: repeat(2, 1fr); } }
640
763
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
641
764
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
642
765
  .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
@@ -645,6 +768,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
645
768
  .card-value.orange { color: #f97316; }
646
769
  .card-value.purple { color: #a855f7; }
647
770
  .card-value.cyan { color: #38bdf8; }
771
+ .card-value.red { color: #f87171; }
648
772
  .card-subtext { font-size: 0.8rem; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
649
773
  .card-subtext.green { color: #10b981; }
650
774
  .card-subtext.orange { color: #f97316; }
@@ -692,12 +816,20 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
692
816
  <div class="cards-wrapper">
693
817
  <div class="metric-card">
694
818
  <div class="card-title">Apdex Score (T: ${config.apdexT}ms)</div>
695
- <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Target Verified)</span></div>
819
+ <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Verified)</span></div>
696
820
  </div>
697
821
  <div class="metric-card">
698
822
  <div class="card-title">Throughput</div>
699
823
  <div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
700
824
  </div>
825
+ <div class="metric-card">
826
+ <div class="card-title">Minimum Response Time</div>
827
+ <div class="card-value green">${cards.minLatency.toFixed(1)}<span class="unit">ms</span></div>
828
+ </div>
829
+ <div class="metric-card">
830
+ <div class="card-title">Maximum Response Time</div>
831
+ <div class="card-value red">${cards.maxLatency.toFixed(1)}<span class="unit">ms</span></div>
832
+ </div>
701
833
  <div class="metric-card">
702
834
  <div class="card-title">Network Bandwidth</div>
703
835
  <div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
@@ -804,8 +936,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
804
936
  <th>Throughput</th>
805
937
  <th>Avg Latency</th>
806
938
  <th>P95 Latency</th>
807
- <th>Simulated LCP</th>
808
- <th>Error Rate</th>
939
+ <th>Min / Max Floor</th>
940
+ <th>RESET Rate</th>
809
941
  <th>Status</th>
810
942
  </tr>
811
943
  </thead>
@@ -817,14 +949,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
817
949
  <td>${h.throughput.toFixed(0)} req/sec</td>
818
950
  <td>${h.avgLatency.toFixed(1)} ms</td>
819
951
  <td>${h.p95Latency.toFixed(1)} ms</td>
820
- <td>${h.lcp.toFixed(0)} ms</td>
821
- <td>${(h.errorRate * 100).toFixed(2)}%</td>
952
+ <td><small>${h.minLatency.toFixed(1)}ms / ${h.maxLatency.toFixed(1)}ms</small></td>
953
+ <td>${h.econnresetRate.toFixed(2)}%</td>
822
954
  <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
823
955
  </tr>`;
824
956
  }).join("")}
825
957
  </tbody>
826
958
  </table>
827
959
  </div>
960
+ ${connectionDiagnosticsPanelHtml}
828
961
  ${liveAdjusterHtml}
829
962
  ${rightSizingHtml}
830
963
  ${logClustersHtml}
@@ -844,7 +977,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
844
977
  const baseThroughput = ${cards.throughput};
845
978
  const baseLatency = ${cards.ttfb};
846
979
 
847
- // Live Concurrency Adjuster interactive client logic
848
980
  const slider = document.getElementById('concurrencySlider');
849
981
  const sliderVal = document.getElementById('sliderValue');
850
982
  const simThroughput = document.getElementById('simThroughput');
@@ -929,8 +1061,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
929
1061
  },
930
1062
  scales: {
931
1063
  x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
932
- y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
933
- y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
1064
+ yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
1065
+ yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
934
1066
  }
935
1067
  }
936
1068
  });
@@ -988,8 +1120,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
988
1120
  }
989
1121
  }
990
1122
  function printUsage() {
991
- console.log(`Blaze Core Performance Test CLI Engine
992
- Options:
993
- --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms>`);
1123
+ console.log(`Blaze Core Performance Test CLI Engine\\nOptions:\\n --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms>`);
994
1124
  }
995
1125
  main().catch(console.error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.1.7",
3
+ "version": "3.1.10",
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",