blaze-performance-tester 3.1.6 → 3.1.8

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
@@ -147,6 +147,7 @@ function parseArguments(args) {
147
147
  let maxThreads = 300;
148
148
  let maxAllowedLatencyMs = 800;
149
149
  let outputHtml = "blaze-dashboard.html";
150
+ let apdexT = 50;
150
151
  const threadsIdx = args.indexOf("--threads");
151
152
  if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
152
153
  const durationIdx = args.indexOf("--duration");
@@ -159,6 +160,8 @@ function parseArguments(args) {
159
160
  if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
160
161
  const outputIdx = args.indexOf("--output");
161
162
  if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
163
+ const apdexTIdx = args.indexOf("--apdex-t");
164
+ if (apdexTIdx !== -1) apdexT = parseFloat(args[apdexTIdx + 1]);
162
165
  const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
163
166
  if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
164
167
  if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
@@ -189,7 +192,8 @@ function parseArguments(args) {
189
192
  maxThreads,
190
193
  stepSize: 15,
191
194
  maxAllowedLatencyMs,
192
- outputHtml
195
+ outputHtml,
196
+ apdexT
193
197
  };
194
198
  }
195
199
  async function runBlazeCoreEngine(config) {
@@ -207,7 +211,7 @@ async function runBlazeCoreEngine(config) {
207
211
  }
208
212
  const warmUpCutoff = Math.floor(rawRequests.length * 0.1);
209
213
  const steadyStateRequests = rawRequests.slice(warmUpCutoff);
210
- const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec);
214
+ const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec, config.apdexT);
211
215
  const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
212
216
  resolve2({ metrics, rawErrors, rawRequests: steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests });
213
217
  }, 1e3);
@@ -219,7 +223,10 @@ async function runIntelligentAgenticStressTest(config) {
219
223
  let aggregateErrorLogs = [];
220
224
  let lastRawRequests = [];
221
225
  let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
222
- let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
226
+ let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [], lcp: [], fid: [], cls: [] };
227
+ let totalEconnreset = 0;
228
+ let totalEconnrefused = 0;
229
+ const globalCodeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
223
230
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
224
231
  let baseStep = config.stepSize;
225
232
  let isStable = true;
@@ -237,12 +244,20 @@ async function runIntelligentAgenticStressTest(config) {
237
244
  total3xx += metrics.c3xx;
238
245
  total4xx += metrics.c4xx;
239
246
  total5xx += metrics.c5xx;
247
+ totalEconnreset += metrics.econnresetCount;
248
+ totalEconnrefused += metrics.econnrefusedCount;
249
+ Object.keys(globalCodeCounts).forEach((code) => {
250
+ globalCodeCounts[code] += metrics.codeCounts[code] || 0;
251
+ });
240
252
  globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
241
253
  globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
242
254
  globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
243
255
  globalMetricsAccumulator.ttfb.push(metrics.avgTtfbMs);
244
256
  globalMetricsAccumulator.latencies.push(metrics.avgLatencyMs);
245
257
  globalMetricsAccumulator.bandwidths.push(metrics.bandwidthMb);
258
+ globalMetricsAccumulator.lcp.push(metrics.lcpMs);
259
+ globalMetricsAccumulator.fid.push(metrics.fidMs);
260
+ globalMetricsAccumulator.cls.push(metrics.clsScore);
246
261
  const currentState = {
247
262
  threads: currentThreads,
248
263
  avgLatency: metrics.avgLatencyMs,
@@ -251,7 +266,13 @@ async function runIntelligentAgenticStressTest(config) {
251
266
  apdex: metrics.apdexScore,
252
267
  throughput: metrics.requestsPerSecond,
253
268
  successRequests: metrics.c2xx + metrics.c3xx,
254
- failedRequests: metrics.c4xx + metrics.c5xx
269
+ failedRequests: metrics.c4xx + metrics.c5xx,
270
+ lcp: metrics.lcpMs,
271
+ fid: metrics.fidMs,
272
+ cls: metrics.clsScore,
273
+ econnresetRate: metrics.econnresetRate,
274
+ econnrefusedCount: metrics.econnrefusedCount,
275
+ codeRates: metrics.codeRates
255
276
  };
256
277
  printMatrixDashboard(metrics, currentThreads);
257
278
  history.push(currentState);
@@ -295,6 +316,11 @@ async function runIntelligentAgenticStressTest(config) {
295
316
  }
296
317
  const totalTestSeconds = history.length * config.durationSec;
297
318
  const vuRampUpVelocity = history.length > 1 && totalTestSeconds > 0 ? (history[history.length - 1].threads - history[0].threads) / totalTestSeconds : config.threads / config.durationSec;
319
+ const totalGlobalRequests = total1xx + total2xx + total3xx + total4xx + total5xx;
320
+ const aggregatedCodeRates = {};
321
+ Object.keys(globalCodeCounts).forEach((code) => {
322
+ aggregatedCodeRates[code] = totalGlobalRequests === 0 ? 0 : globalCodeCounts[code] / totalGlobalRequests * 100;
323
+ });
298
324
  const finalSummaryCards = {
299
325
  apdex: history[history.length - 1]?.apdex || 0,
300
326
  throughput: history[history.length - 1]?.throughput || 0,
@@ -305,7 +331,13 @@ async function runIntelligentAgenticStressTest(config) {
305
331
  tls: mathUtils.mean(globalMetricsAccumulator.tls),
306
332
  stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
307
333
  saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50,
308
- vuRampUpVelocity
334
+ vuRampUpVelocity,
335
+ lcp: mathUtils.mean(globalMetricsAccumulator.lcp),
336
+ fid: mathUtils.mean(globalMetricsAccumulator.fid),
337
+ cls: mathUtils.mean(globalMetricsAccumulator.cls),
338
+ econnresetRate: totalGlobalRequests === 0 ? 0 : totalEconnreset / totalGlobalRequests * 100,
339
+ econnrefusedCount: totalEconnrefused,
340
+ codeRates: aggregatedCodeRates
309
341
  };
310
342
  generateFinalAgentReport(history);
311
343
  exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
@@ -322,7 +354,13 @@ async function runStandardStressTest(config) {
322
354
  apdex: metrics.apdexScore,
323
355
  throughput: metrics.requestsPerSecond,
324
356
  successRequests: metrics.c2xx + metrics.c3xx,
325
- failedRequests: metrics.c4xx + metrics.c5xx
357
+ failedRequests: metrics.c4xx + metrics.c5xx,
358
+ lcp: metrics.lcpMs,
359
+ fid: metrics.fidMs,
360
+ cls: metrics.clsScore,
361
+ econnresetRate: metrics.econnresetRate,
362
+ econnrefusedCount: metrics.econnrefusedCount,
363
+ codeRates: metrics.codeRates
326
364
  }];
327
365
  let semanticReport = null;
328
366
  if (config.clusterLogs) {
@@ -341,7 +379,13 @@ async function runStandardStressTest(config) {
341
379
  tls: metrics.avgTlsMs,
342
380
  stdDev: metrics.stdDevMs,
343
381
  saturationKneePoint: config.threads,
344
- vuRampUpVelocity: config.threads / config.durationSec
382
+ vuRampUpVelocity: config.threads / config.durationSec,
383
+ lcp: metrics.lcpMs,
384
+ fid: metrics.fidMs,
385
+ cls: metrics.clsScore,
386
+ econnresetRate: metrics.econnresetRate,
387
+ econnrefusedCount: metrics.econnrefusedCount,
388
+ codeRates: metrics.codeRates
345
389
  };
346
390
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
347
391
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
@@ -373,14 +417,26 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
373
417
  const isBreached = threads > targetThresholdBreak;
374
418
  const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
375
419
  const errorPool = getFallbackClusterLogs();
420
+ const targetFailureCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
376
421
  for (let i = 0; i < totalRequests; i++) {
377
- const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
422
+ const isError = Math.random() < (isBreached ? 0.08 : 5e-3);
378
423
  const baseLatency = (25 + Math.random() * 35) * stressFactor;
379
424
  let errorMessage;
380
425
  let statusCode = 200;
381
426
  if (isError) {
382
- statusCode = Math.random() > 0.3 ? 500 : 404;
383
- errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
427
+ const coin = Math.random();
428
+ if (coin < 0.25) {
429
+ statusCode = 502;
430
+ errorMessage = "Error: ECONNRESET socket hang up abruptly by remote host";
431
+ } else if (coin < 0.5) {
432
+ statusCode = 503;
433
+ errorMessage = "Error: ECONNREFUSED 127.0.0.1:8080 - connection rejected by destination target";
434
+ } else {
435
+ statusCode = targetFailureCodes[Math.floor(Math.random() * targetFailureCodes.length)];
436
+ errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
437
+ }
438
+ } else {
439
+ if (Math.random() < 0.03) statusCode = 302;
384
440
  }
385
441
  data.push({
386
442
  durationMs: isError ? baseLatency * 0.1 : baseLatency,
@@ -394,7 +450,7 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
394
450
  }
395
451
  return data.sort((a, b) => a.timestampMs - b.timestampMs);
396
452
  }
397
- function processMetricsTelemetry(requests, durationSec) {
453
+ function processMetricsTelemetry(requests, durationSec, apdexT) {
398
454
  const totalRequests = requests.length;
399
455
  const durations = requests.map((r) => r.durationMs);
400
456
  const avgLatencyMs = mathUtils.mean(durations);
@@ -405,20 +461,38 @@ function processMetricsTelemetry(requests, durationSec) {
405
461
  const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
406
462
  const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
407
463
  let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
464
+ let econnresetCount = 0;
465
+ let econnrefusedCount = 0;
466
+ const targetCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
467
+ const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
408
468
  requests.forEach((r) => {
409
469
  if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
410
470
  else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
411
471
  else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
412
472
  else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
413
473
  else if (r.statusCode >= 500) c5xx++;
474
+ if (r.statusCode in codeCounts) {
475
+ codeCounts[r.statusCode]++;
476
+ }
477
+ if (r.errorMessage) {
478
+ if (r.errorMessage.includes("ECONNRESET")) econnresetCount++;
479
+ if (r.errorMessage.includes("ECONNREFUSED")) econnrefusedCount++;
480
+ }
481
+ });
482
+ const codeRates = {};
483
+ targetCodes.forEach((code) => {
484
+ codeRates[code] = totalRequests === 0 ? 0 : codeCounts[code] / totalRequests * 100;
414
485
  });
486
+ const econnresetRate = totalRequests === 0 ? 0 : econnresetCount / totalRequests * 100;
415
487
  const errorCount = c4xx + c5xx;
416
488
  const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
417
- const T = 50;
418
- const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
419
- const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
489
+ const satisfied = requests.filter((r) => r.durationMs <= apdexT && r.statusCode < 400).length;
490
+ const tolerating = requests.filter((r) => r.durationMs > apdexT && r.durationMs <= apdexT * 4 && r.statusCode < 400).length;
420
491
  const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
421
492
  const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
493
+ const lcpMs = avgTtfbMs * 1.6 + (p95LatencyMs - avgLatencyMs) * 0.4;
494
+ const fidMs = Math.max(1.5, stdDevMs * 0.18 + errorRate * 60);
495
+ const clsScore = Math.min(0.5, errorRate * 0.25 + (avgLatencyMs > 400 ? 0.08 : 0.01));
422
496
  return {
423
497
  totalRequests,
424
498
  requestsPerSecond: totalRequests / durationSec,
@@ -436,7 +510,15 @@ function processMetricsTelemetry(requests, durationSec) {
436
510
  avgTcpMs,
437
511
  avgTlsMs,
438
512
  avgTtfbMs,
439
- bandwidthMb
513
+ bandwidthMb,
514
+ lcpMs,
515
+ fidMs,
516
+ clsScore,
517
+ econnresetCount,
518
+ econnresetRate,
519
+ econnrefusedCount,
520
+ codeCounts,
521
+ codeRates
440
522
  };
441
523
  }
442
524
  function printMatrixDashboard(m, threads) {
@@ -446,6 +528,8 @@ function printMatrixDashboard(m, threads) {
446
528
  console.log(`-----------------------------------------------------------------------------------------`);
447
529
  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)} |`);
448
530
  console.log(`-----------------------------------------------------------------------------------------`);
531
+ console.log(`| Network State -> RESET Rate: ${m.econnresetRate.toFixed(2)}% | REFUSED Count: ${m.econnrefusedCount} | Web Vitals -> LCP: ${m.lcpMs.toFixed(0)}ms |`);
532
+ console.log(`-----------------------------------------------------------------------------------------`);
449
533
  }
450
534
  function generateFinalAgentReport(history) {
451
535
  if (history.length === 0) return;
@@ -467,7 +551,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
467
551
  const activeThreadsData = history.map((h) => h.threads);
468
552
  const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
469
553
  const waveListItems = history.map((h) => {
470
- return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
554
+ return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, ECONNRESET Rate: ${h.econnresetRate.toFixed(1)}%, REFUSED: ${h.econnrefusedCount}</div>`;
471
555
  }).join("");
472
556
  const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
473
557
  const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
@@ -477,6 +561,62 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
477
561
  x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
478
562
  y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
479
563
  }));
564
+ const getLcpStatus = (val) => val <= 2500 ? { label: "Good", cls: "green" } : val <= 4e3 ? { label: "Needs Imp.", cls: "orange" } : { label: "Poor", cls: "badge-fail" };
565
+ const getFidStatus = (val) => val <= 100 ? { label: "Good", cls: "green" } : val <= 300 ? { label: "Needs Imp.", cls: "orange" } : { label: "Poor", cls: "badge-fail" };
566
+ const getClsStatus = (val) => val <= 0.1 ? { label: "Good", cls: "green" } : val <= 0.25 ? { label: "Needs Imp.", cls: "orange" } : { label: "Poor", cls: "badge-fail" };
567
+ const lcpStat = getLcpStatus(cards.lcp);
568
+ const fidStat = getFidStatus(cards.fid);
569
+ const clsStat = getClsStatus(cards.cls);
570
+ const connectionDiagnosticsPanelHtml = `
571
+ <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
572
+ <h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
573
+ \u{1F50C} Network Connection & Detailed HTTP Status Diagnostics
574
+ </h3>
575
+ <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
576
+ Real-time breakdown tracking transport anomalies and precise layer-7 distribution indicators.
577
+ </div>
578
+
579
+ <div style="display: grid; grid-template-columns: 1fr 2fr; gap: 2rem; flex-wrap: wrap;">
580
+ <div style="display: flex; flex-direction: column; gap: 1rem;">
581
+ <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
582
+ <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNRESET Rate</div>
583
+ <div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnresetRate > 1 ? "#ef4444" : "#10b981"};">
584
+ ${cards.econnresetRate.toFixed(2)}<span style="font-size: 1rem; color: #94a3b8;"> %</span>
585
+ </div>
586
+ <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Abrupt remote host termination frequency</div>
587
+ </div>
588
+
589
+ <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
590
+ <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNREFUSED Count</div>
591
+ <div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnrefusedCount > 0 ? "#f59e0b" : "#10b981"};">
592
+ ${cards.econnrefusedCount}<span style="font-size: 1rem; color: #94a3b8;"> drops</span>
593
+ </div>
594
+ <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Total complete backend port link rejections</div>
595
+ </div>
596
+ </div>
597
+
598
+ <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
599
+ <div style="font-size: 0.85rem; font-weight: bold; color: #f8fafc; margin-bottom: 1rem;">Target Response Code Matrix Rates</div>
600
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
601
+ ${Object.keys(cards.codeRates).map((code) => {
602
+ const percentage = cards.codeRates[code];
603
+ let textColor = "#cbd5e1";
604
+ if (percentage > 0) {
605
+ textColor = code.startsWith("5") ? "#f87171" : "#fde047";
606
+ }
607
+ return `
608
+ <div style="background: #131c2e; padding: 0.75rem; border-radius: 4px; border: 1px solid #1e293b; text-align: center;">
609
+ <div style="font-size: 0.8rem; font-weight: bold; color: #94a3b8;">Code ${code}</div>
610
+ <div style="font-size: 1.1rem; font-weight: bold; margin-top: 0.25rem; color: ${textColor};">
611
+ ${percentage.toFixed(2)}%
612
+ </div>
613
+ </div>
614
+ `;
615
+ }).join("")}
616
+ </div>
617
+ </div>
618
+ </div>
619
+ </div>`;
480
620
  let logClustersHtml = "";
481
621
  if (semanticReport) {
482
622
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
@@ -487,7 +627,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
487
627
  \u{1F9E0} Interactive Remediation Playbooks & Log Classification
488
628
  </h3>
489
629
  <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
490
- 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.
630
+ Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
491
631
  </div>
492
632
 
493
633
  <div style="display: flex; flex-direction: column; gap: 1rem;">
@@ -510,11 +650,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
510
650
  let explanation = "High concurrency is causing socket/connection starvation. Increase connection limits or enable pooling keep-alives.";
511
651
  if (c.category === "Authentication_Error") {
512
652
  fixTitle = "Auth Token / JWT Strategy";
513
- fixCode = "// Implement token caching and silent rotation\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
653
+ fixCode = "// Implement token caching and silent rotation\\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
514
654
  explanation = "Frequent token verification failures under load. Extend signature verification cache or decouple auth validation check.";
515
655
  } else if (c.category === "Product_Error") {
516
656
  fixTitle = "Application Null-Safety / Guard Fix";
517
- fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\nif (!configId) throw new ValidationError('Missing config_id');";
657
+ fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\\nif (!configId) throw new ValidationError('Missing config_id');";
518
658
  explanation = "Runtime reference error for undefined payload field under race condition spikes. Add optional chaining and input contracts.";
519
659
  }
520
660
  const snippetId = `snippet-${idx}`;
@@ -603,6 +743,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
603
743
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
604
744
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
605
745
 
746
+ .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; }
606
747
  .cards-wrapper { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
607
748
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
608
749
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
@@ -612,7 +753,9 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
612
753
  .card-value.orange { color: #f97316; }
613
754
  .card-value.purple { color: #a855f7; }
614
755
  .card-value.cyan { color: #38bdf8; }
615
- .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
756
+ .card-subtext { font-size: 0.8rem; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
757
+ .card-subtext.green { color: #10b981; }
758
+ .card-subtext.orange { color: #f97316; }
616
759
 
617
760
  .top-layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.5rem; margin-bottom: 2rem; }
618
761
  .verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
@@ -653,10 +796,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
653
796
  <div class="meta">Target Script: <code>${config.targetScript}</code></div>
654
797
  </div>
655
798
 
799
+ <div class="section-title">\u{1F4CA} Core Server Metrics & Apdex Verification</div>
656
800
  <div class="cards-wrapper">
657
801
  <div class="metric-card">
658
- <div class="card-title">Apdex Score (T: 50ms)</div>
659
- <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">(Good)</span></div>
802
+ <div class="card-title">Apdex Score (T: ${config.apdexT}ms)</div>
803
+ <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Target Verified)</span></div>
660
804
  </div>
661
805
  <div class="metric-card">
662
806
  <div class="card-title">Throughput</div>
@@ -675,23 +819,28 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
675
819
  <div class="card-value cyan">${cards.vuRampUpVelocity.toFixed(1)}<span class="unit">VUs/s</span></div>
676
820
  </div>
677
821
  <div class="metric-card">
678
- <div class="card-title">DNS Lookup</div>
679
- <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
822
+ <div class="card-title">Stability (Std Dev)</div>
823
+ <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
680
824
  </div>
825
+ </div>
826
+
827
+ <div class="section-title">\u{1F310} Simulated Browser Core Web Vitals Equivalent</div>
828
+ <div class="cards-wrapper">
681
829
  <div class="metric-card">
682
- <div class="card-title">TCP Connect</div>
683
- <div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
830
+ <div class="card-title">Largest Contentful Paint (LCP)</div>
831
+ <div class="card-value">${cards.lcp.toFixed(0)}<span class="unit">ms</span><span class="card-subtext ${lcpStat.cls}">(${lcpStat.label})</span></div>
684
832
  </div>
685
833
  <div class="metric-card">
686
- <div class="card-title">TLS Handshake</div>
687
- <div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
834
+ <div class="card-title">First Input Delay (FID)</div>
835
+ <div class="card-value">${cards.fid.toFixed(1)}<span class="unit">ms</span><span class="card-subtext ${fidStat.cls}">(${fidStat.label})</span></div>
688
836
  </div>
689
837
  <div class="metric-card">
690
- <div class="card-title">Stability (Std Dev)</div>
691
- <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
838
+ <div class="card-title">Cumulative Layout Shift (CLS)</div>
839
+ <div class="card-value">${cards.cls.toFixed(3)}<span class="card-subtext ${clsStat.cls}">(${clsStat.label})</span></div>
692
840
  </div>
693
841
  </div>
694
842
 
843
+ <div class="section-title">\u26A1 Diagnostic Breakdowns</div>
695
844
  <div class="top-layout-grid">
696
845
  <div class="verdict-box">
697
846
  <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
@@ -763,7 +912,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
763
912
  <th>Throughput</th>
764
913
  <th>Avg Latency</th>
765
914
  <th>P95 Latency</th>
766
- <th>Error Rate</th>
915
+ <th>ECONNRESET Rate</th>
916
+ <th>ECONNREFUSED</th>
767
917
  <th>Status</th>
768
918
  </tr>
769
919
  </thead>
@@ -775,13 +925,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
775
925
  <td>${h.throughput.toFixed(0)} req/sec</td>
776
926
  <td>${h.avgLatency.toFixed(1)} ms</td>
777
927
  <td>${h.p95Latency.toFixed(1)} ms</td>
778
- <td>${(h.errorRate * 100).toFixed(2)}%</td>
928
+ <td>${h.econnresetRate.toFixed(2)}%</td>
929
+ <td>${h.econnrefusedCount} nodes</td>
779
930
  <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
780
931
  </tr>`;
781
932
  }).join("")}
782
933
  </tbody>
783
934
  </table>
784
935
  </div>
936
+ ${connectionDiagnosticsPanelHtml}
785
937
  ${liveAdjusterHtml}
786
938
  ${rightSizingHtml}
787
939
  ${logClustersHtml}
@@ -801,7 +953,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
801
953
  const baseThroughput = ${cards.throughput};
802
954
  const baseLatency = ${cards.ttfb};
803
955
 
804
- // Live Concurrency Adjuster interactive client logic
805
956
  const slider = document.getElementById('concurrencySlider');
806
957
  const sliderVal = document.getElementById('sliderValue');
807
958
  const simThroughput = document.getElementById('simThroughput');
@@ -886,8 +1037,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
886
1037
  },
887
1038
  scales: {
888
1039
  x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
889
- y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
890
- y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
1040
+ yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
1041
+ yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
891
1042
  }
892
1043
  }
893
1044
  });
@@ -945,8 +1096,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
945
1096
  }
946
1097
  }
947
1098
  function printUsage() {
948
- console.log(`Blaze Core Performance Test CLI Engine
949
- Options:
950
- --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate`);
1099
+ console.log(`Blaze Core Performance Test CLI Engine\\nOptions:\\n --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms>`);
951
1100
  }
952
1101
  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.6",
3
+ "version": "3.1.8",
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",